Saturday, July 4, 2026

STM32 Development Log 8: State flow diagram, July 4 saturday.

Active Magnetic Bearing Controller Software Design Document

Active Magnetic Bearing Controller

Software Architecture and Function Reference

This document was generated from the supplied main.c. It documents the overall architecture, state machine, module interactions, and the principal functions. It is suitable as a Blogger article and can be extended as the project evolves.

1. Overview

The firmware implements a closed-loop Active Magnetic Bearing (AMB) controller on an STM32H753. The software controls five magnetic bearing axes using AD7606 ADCs, an AD5361 16-channel DAC, two SH1106 OLED displays, and a finite state machine for safe operation and parameter tuning.

2. Hardware Interfaces

PeripheralPurpose
SPI2AD7606 ADC acquisition
SPI6AD5361 DAC output
I2C2/I2C3Dual SH1106 OLED displays
TIM14300 µs control loop scheduler
TIM13250 ms heartbeat, menu scan and long-press detection
GPIO EXTIImmediate state transitions

3. Software Architecture

                 main()

                   |
        ------------------------
        |                      |
 Hardware Init           Variable Init
        |                      |
        -----------+------------
                    |
             SAFE_MAIN_MENU
                    |
        +-----------+-----------+
        |                       |
   TIM13 Interrupt        Main Loop
        |                       |
        |                 OLED Update
        |
   Menu Navigation
        |
        +-----> RUN_STATE
                    |
              TIM14 Interrupt
                    |
      Read ADC -> Control -> DAC

4. Finite State Machine

SAFE_MAIN_MENU
    |
    +--> Axis Menu
             |
             +--> Parameter Menu
                      |
                      +--> Parameter Edit

SAFE_MAIN_MENU
    |
    +--> Orbit
    |
    +--> Manual
    |
    +--> ADC Display
    |
    +--> PA Gain

Long SW4
    |
    V
RUN_STATE

SW1 during RUN
    |
    V
SAFE_MAIN_MENU

5. Main Program Flow

  1. HAL initialization
  2. Clock configuration
  3. Peripheral initialization
  4. SPI speed configuration
  5. Timer configuration
  6. OLED initialization
  7. DAC reset and initialization
  8. ADC reset
  9. Controller parameter initialization
  10. Enter infinite loop

6. Function Call Hierarchy

main()
 ├── initializeAxisControlValueDefaults()
 ├── write16valuesToDac5361_v2()
 ├── HAL_TIM_Base_Start_IT()
 └── while()
      ├── displayRunState()
      └── displaySafeState()

TIM13 ISR
 ├── readAdc1Spi3BrdSpi2StmCh8ToCh1()
 ├── tuneParameterFromPot1()
 ├── updateSafeMenuSelection()
 ├── initializeControllerStates()
 ├── controlUpdateCompute()
 └── write16valuesToDac5361_v2()

TIM14 ISR
 ├── readAdc1Spi3BrdSpi2StmCh8ToCh1()
 ├── controlUpdateCompute()
 └── write16valuesToDac5361_v2()

controlUpdateCompute()
 └── updateOneAxis()
       ├── updateErrorHistory()
       └── PD_Compute()

7. Major Functions

FunctionDescription
initializeAxisControlValueDefaults()Loads default controller gains and bias currents.
initializeControllerStates()Synchronizes controller history before entering RUN.
processSwInp1interrPress()Processes FSM transitions from SW1.
enterParameterEdit()Loads parameter limits and current value.
updateSafeMenuSelection()Maps POT1 voltage into menu indices.
controlUpdateCompute()Updates all five AMB axes.
updateOneAxis()Performs one axis position-control update.
PD_Compute()Computes proportional-derivative controller output.
write16valuesToDac5361_v2()Writes all 16 DAC channels then pulses LDAC.
readAdc1Spi3BrdSpi2StmCh8ToCh1()Reads eight ADC channels from AD7606.
displayRunState()Displays live controller status.
displaySafeState()Dispatches menu drawing based on FSM state.
drawMainMenu()Main safe-state menu.
drawAxisMenu()Axis selection screen.
drawParameterMenu()Displays parameter list and current values.
drawParameterEdit()Interactive parameter tuning screen.
saveEditedParameter()Stores edited parameter into the selected axis.

8. Control Loop

Sensor mV
   |
sensorGain
   |
Position (mm)
   |
Reference
   |
Error
   |
PD Controller
   |
Bias Current
   |
Current-to-Voltage Gain
   |
DAC Voltage
   |
Power Amplifier

9. Safety Features

  • System always powers up in SAFE mode.
  • RUN mode requires a continuous 3-second SW4 press.
  • SW1 immediately exits RUN mode.
  • DAC outputs are forced to ground in SAFE mode using the AD5361 CLEAR input.
  • Controller histories are initialized before closed-loop operation.

10. Future Extensions

  • Orbit control implementation.
  • Manual actuator drive mode.
  • PA gain calibration.
  • Derivative filtering.
  • Integral control.
  • Persistent parameter storage in Flash.
  • DMA-based ADC and DAC transfers.

Sunday, June 28, 2026

STM32 Development Log 7: TIM14 TIM13 priority setup

GPIO  

 

ButtonMethod
PF10EXTI
PC0EXTI
PC1EXTI
PA0Poll every 5–10 ms using some timer or leave. Cant interrupt using both PC1 and PA0
PH2EXTI
PH3EXTI

TIMER13 for LED

Enable NVIC interrupt

 

 TIMER 14

Enable Nvic
Tim14 should be 0, control loop highest priority
Tim13 can be 5
Systick last 15 
 
 
 

Sunday, June 21, 2026

STM32 Development Log 6 : ADC sampling not working

ADC 1 and 2 pins for spi 3 (board) SPI2 in the MCU:

Supply is from AN_5V, requires 12V in power supply.
ADC1 reset
ADC1 conv start = J1 2= PE3
ADC CS 1 = J2 12 = PI0
SPI3 clock
ADC1 busy = J1 10 = PI10
SPI3 MISO = J2 8 = SPI2 MISO of MCU  = PI2
 
ADC2 reset
ADC2 conv start= J1 4= PI8
ADC CS 2 = J2 6 = actually SPI3 board MOSI pin  = PI3
SPI3 clock
ADC2 busy = J1 12 =PI11
SPI3 MISO

Base board side:

 

Controller side


 

Original SPI2 config from SPI-ADC-DAC demo:

 

 PI1 PI2 as clock and MISO for SPI2


ADCICChannels
ADC1ADS7606 (U9)AN0–AN7
ADC2ADS7606 (U10)AN8–AN15

All 8 channels on each ADS7606 are sampled simultaneously
 

Low pass filter at the input line of each adc channel:

R = 100 Ω
C = 0.1 µF
16 kHz low-pass anti-alias filter
 

 

Pin Purpose
PI8 ADC1 conversion trigger (CONVST)
PI11 ADC1 BUSY signal
PI9 ADC2 conversion trigger (CONVST)
PI10 ADC2 BUSY signal
PI0 ADC1 SPI chip select
PI3 ADC2 SPI chip select

STM32 Development Log: Timer settings

Always backup main.c before generating code. 

 HCLK = 200 MHz

  1. D2PPRE1 = /2, again multiplied by 2 for APB1 Timer Clock , APB1 Timer Clock alone is back to 200
  2. APB1 Peripheral Clock = 100 MHz, APB1 Peripheral Clock is not multiplied by 2, it is 100 only
  3. APB1 Timer Clock = 200 MHz
  4. Prescaler = 199, Counter Clock = 1 MHz, Auto Reload (Period) = 199
  5. 200e6/200 prescaler = 1e6, 1e6/200 prescaler = 5000 Hz
  6. control loop Period = 200 µs, update Frequency = 5000 Hz
 

Enable interrupts from timer 14

 

 Clock configuration

 
 5kHz update in oscilloscope:

 

Saturday, June 20, 2026

STM32 Development Log 5: Current consumption

When running the  Switches_LED_Demo program,]

Current consumption increase by 100mA when SW3 is pressed. 

  1. When only heart beat LED is running, 0.179 Ampere @12V
  2. SW1:With LD5 on 0.187 Ampere = 8 mA increase over case 1
  3. SW2:With LD4 on 0.195 Ampere = 8 mA increase over case 2
  4. SW3:With LD3 on 0.305 Ampere =  110 mA increase over case 3 Why? SW3 switches PI14 and PI15. PI14 is LD3. PI15 is J3 connector, pin 38, switching relay  1. However this cannot add 100mA to the current consumption. 
 

After disable PI15 relay output in software, the current consumption is correct:

  • When only green heart beat LED is running, 0.175 Ampere @12V
  • SW1:With LD5 red on 0.184 Ampere = 9 mA increase over case 1
  • SW2:With LD4 red on 0.192 Ampere = 8 mA increase over case 2
  • SW3:With LD3 red on 0.193 Ampere = 1 mA increase over case 3 
  • SW4:With LD2 green on 0.195 Ampere = 2 mA increase over case 4
  • SW5:With LD1 green on 0.197 Ampere = 2 mA increase over case 5
  • SW6:With SW_LED green on 0.198 Ampere = 2 mA increase over case 6, in the control module.
  • if(button3 == 1)

    {

    if(button3_state == 0)

    {

    HAL_GPIO_WritePin(GPIOI, GPIO_PIN_14, GPIO_PIN_SET);

    button3_state = 1;

    // PI 15 is relay 1

    // This code is for testing only

    // HAL_GPIO_WritePin(GPIOI, GPIO_PIN_15, GPIO_PIN_SET);

    }

    else

    {

    HAL_GPIO_WritePin(GPIOI, GPIO_PIN_14, GPIO_PIN_RESET);

    button3_state = 0;

    // PI 15 is relay 1

    // This code is for testing only

    // HAL_GPIO_WritePin(GPIOI, GPIO_PIN_15, GPIO_PIN_RESET);


    }

    button3 = 0 ;


    }


     

    Comments

    1. LD2 , triggered by SW4, PA0 , needs long press for the LED to switch states, it is not interrupt driven, it is polled.
    2.  LD1, triggered by SW5, increases current consumption by only 3mA
    3. SW_LD10, triggered by SW6,  increases current consumption by only 4mA, SW_LD10 is physically located on the compute module, not on the base board.

    STM32 Development Log 4: Heart beat LED

    System clock setting:

    RCC_OscInitStruct.PLL.PLLM = 4;

    RCC_OscInitStruct.PLL.PLLN = 50;

    RCC_OscInitStruct.PLL.PLLP = 2;

     

    Timer duration set in tim.c

    /* USER CODE END TIM13_Init 1 */

    htim13.Instance = TIM13;

    htim13.Init.Prescaler = 9999;

    htim13.Init.CounterMode = TIM_COUNTERMODE_UP;

    htim13.Init.Period = 9999;

    htim13.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4;

    htim13.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

    Interrupt Frequency =
    TIM13 Clock / ((Prescaler + 1) × (Period + 1))

     PF2 Toggle  code in main.c:

    if(htim->Instance == TIM13)

    {


    if(heart_beat == 0)

    {

    heart_beat = 1 ;

    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_SET);

    }

    else

    {


    heart_beat = 0 ;

    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_RESET);

    }



     

    STM32 Development Log 3: Button details

    Button Summary

    Button Net Name MCU Pin Input Type Detection Method Interrupt Type Pull-up Type Pull-up Resistor
    SW1 SW_INP1 PF10 Digital Input EXTI Interrupt Falling Edge External R62 = 10 kΩ
    SW2 SW_INP2 PC0 Digital Input EXTI Interrupt Falling Edge External R61 = 10 kΩ
    SW3 SW_INP3 PC1 Digital Input EXTI Interrupt Falling Edge External R60 = 10 kΩ
    SW4 SW_INP4 PA0 Digital Input TIM7 Polling None External R59 = 10 kΩ
    SW5 SW_INP5 PH2 Digital Input EXTI Interrupt Falling Edge External R57 = 10 kΩ
    SW6 SW_INP6 PH3 Digital Input EXTI Interrupt Falling Edge External R56 = 10 kΩ

    Switch Debounce and Protection Components

    Button Series Resistor Debounce Capacitor TVS / ESD Protection
    SW1 R14 = 100 Ω C10 = 0.1 µF VS6
    SW2 R15 = 100 Ω C11 = 0.1 µF VS7
    SW3 R16 = 100 Ω C12 = 0.1 µF VS8
    SW4 R17 = 100 Ω C13 = 0.1 µF VS11
    SW5 R18 = 100 Ω C14 = 0.1 µF VS9
    SW6 R19 = 100 Ω C15 = 0.1 µF VS10

    Output / LED Summary

    Function MCU Output Pin(s) GPIO Configuration Controlled By
    Heartbeat LED PF2 Digital Output (Push-Pull) TIM13 Interrupt
    Output 1 PI12, PB1 Digital Output (Push-Pull) SW1
    Output 2 PI13, PB2 Digital Output (Push-Pull) SW2
    Output 3 PI14, PI15 Digital Output (Push-Pull) SW3
    Output 4 PF4 Digital Output (Push-Pull) SW4
    Output 5 PH5 Digital Output (Push-Pull) SW5
    Output 6 PK2 Digital Output (Push-Pull) SW6