cancel
Showing results for 
Search instead for 
Did you mean: 

Overcoming problems with center-aligned PWM using burst-mode DMA

FMadi.1
Associate III

As an exercise, I have written a 2-phase induction motor control program using high-level program elements (HAL).  I used a Nucleo-G071, though I'm sure nearly any STM32 family device will work.  Since 2-phase algorithms are not common cookbook items, I started from scratch.  The desired end state is the use of center-aligned PWM using burst-mode DMA and a 3-wire interface.  I encountered problems with asymmetry in the pulse positions of the 3 outputs.  After much coding and experimentation with logic analyzers and oscilloscopes, I came up with a method that yields a clean uniform output.  I used VS Code and GitHub Copilot during the development.  After finishing, I asked GitHub Copilot to summarize the problems and solutions.  Perhaps this may be useful to those who are undertaking similar work.  I am attaching that summary here:

 

# Solving Center-Aligned PWM Asymmetry in STM32 DMA Burst Mode Applications

**A Novel Split-and-Phase-Shift Algorithm for Motor Control**

*Paper developed with assistance from GitHub Copilot*

## Abstract

This paper presents a practical solution to center-aligned PWM asymmetry issues encountered when using STM32 timer DMA burst mode for motor control applications. We describe a novel "split-and-phase-shift" algorithm that eliminates concatenation artifacts in lookup table-based PWM generation while maintaining sinusoidal motor drive characteristics. The solution was implemented and verified on STM32G071 hardware using a 384-element lookup table for 2-phase motor control.

## 1. Introduction

STM32 microcontrollers offer powerful timer peripherals with DMA burst mode capabilities, enabling efficient PWM generation for motor control applications. However, when implementing lookup table-based sinusoidal PWM using center-aligned mode, developers may encounter timing asymmetries that manifest as concatenation artifacts in the PWM output. This paper documents a systematic approach to identifying and resolving these issues.

### 1.1 Problem Statement

Center-aligned PWM mode is essential for motor control applications requiring symmetric pulse generation. However, when DMA updates occur at arbitrary points in the PWM cycle, the resulting output may exhibit:

- Visible concatenation between different duty cycle values
- Asymmetric pulse timing despite center-aligned configuration
- Glitches during transitions between lookup table entries

These issues are particularly problematic in motor control where smooth sinusoidal drive signals are critical for proper operation.

## 2. Technical Background

### 2.1 STM32 Timer DMA Burst Mode

The STM32 timer DMA burst mode allows efficient updating of multiple timer registers (CCR1, CCR2, CCR3) simultaneously. Configuration involves:

```c
// Configure DMA burst base address and length
TIM1->DCR = (TIM_DMABASE_CCR1 << TIM_DCR_DBA_Pos) | ((3-1) << TIM_DCR_DBL_Pos);

// Start DMA transfer from lookup table to timer registers
HAL_DMA_Start_IT(&hdma_tim1_up, (uint32_t)lookup_table, (uint32_t)&TIM1->DMAR, array_size);
```

### 2.2 Center-Aligned Mode Timing

In center-aligned mode, the timer counts up to the period value, then counts down to zero. PWM outputs change state when the counter matches the compare values, creating symmetric pulses around the center point. The critical insight is that DMA updates must be synchronized with the PWM cycle to avoid timing conflicts.

### 2.3 Original Implementation Issues

Initial implementation used a 192-element lookup table with triplets representing {CCR1, CCR2, CCR3} values:

```c
const uint16_t original_table[192] = {
666, 0, 666, // Triplet 1
812, 0, 663, // Triplet 2
// ... more triplets
};
```

Logic analyzer testing revealed concatenation artifacts when transitioning between triplets, particularly visible during alternating duty cycle patterns.

## 3. The Split-and-Phase-Shift Solution

### 3.1 Root Cause Analysis

The concatenation problem stems from DMA timing relative to the PWM cycle. When DMA updates occur during PWM transitions, the resulting output exhibits timing artifacts. The solution requires ensuring DMA updates align properly with the center-aligned PWM timing.

### 3.2 Algorithm Development

Our solution employs a three-step process:

1. **Split each triplet in half**: Each original triplet {A, B, C} becomes two half-triplets {A/2, B/2, C/2}
2. **Handle odd numbers**: For odd values, the larger half comes first
3. **Phase offset**: Move the first half-triplet to the end of the array

### 3.3 Implementation Details

```c
// Original 64-triplet table becomes 384-element table (64 * 6 elements)
const uint16_t SRC_Buf_2Phase[384] = {
// Starting with second half of first triplet for phase alignment
333, 0, 333,

// Each original triplet split: {812, 0, 663} becomes:
406, 0, 332, 406, 0, 331, // First half, second half

// Continue for all 64 original triplets...
// ...

// Phase offset: First half of original first triplet at the end
333, 0, 333
};
```

### 3.4 Mathematical Foundation

For any original triplet {A, B, C}:
- First half: {⌈A/2⌉, ⌈B/2⌉, ⌈C/2⌉}
- Second half: {⌊A/2⌋, ⌊B/2⌋, ⌊C/2⌋}

The phase offset ensures DMA updates occur at optimal points in the PWM cycle, eliminating concatenation artifacts.

## 4. Implementation and Results

### 4.1 Hardware Configuration

- **Microcontroller**: STM32G071RB (NUCLEO-G071RB)
- **Timer**: TIM1 in center-aligned mode 1
- **DMA**: Burst mode updating CCR1, CCR2, CCR3
- **PWM Frequency**: 3.84 kHz (60Hz × 64 pulses)
- **Motor Control**: 2-phase with 90° phase relationship

### 4.2 Verification Method

Testing used both logic analyzer and oscilloscope to verify:
- PWM frequency accuracy
- Center-aligned symmetry
- Elimination of concatenation artifacts
- Proper 90° phase relationship between outputs

### 4.3 Results

The split-and-phase-shift algorithm successfully:
- Eliminated all visible concatenation artifacts
- Maintained proper center-aligned symmetric pulses
- Preserved sinusoidal characteristics for motor control
- Achieved smooth transitions between all lookup table entries

## 5. Application to Motor Control

### 5.1 2-Phase Motor Drive

The implementation provides differential outputs:
- **Phase A**: CH1 - CH3 (0° reference)
- **Phase B**: CH2 - CH3 (90° phase shift)

This configuration enables efficient 2-phase motor control with minimal hardware complexity.

### 5.2 Performance Benefits

- **CPU Efficiency**: DMA handles all PWM updates autonomously
- **Timing Precision**: Hardware-based updates ensure consistent timing
- **Scalability**: Algorithm extends to larger lookup tables
- **Flexibility**: Maintains compatibility with STM32CubeMX code generation

## 6. Comparison with Existing Solutions

### 6.1 STM32 Motor Control SDK

The X-CUBE-MCSDK uses Space Vector PWM (SVPWM) with real-time calculation, avoiding lookup table timing issues entirely. However, this approach requires higher CPU overhead and more complex implementation.

### 6.2 Advantages of Our Approach

- Lower CPU overhead than real-time calculation
- Deterministic timing characteristics
- Simple implementation suitable for cost-sensitive applications
- Compatible with standard STM32 HAL libraries

## 7. Conclusions and Future Work

The split-and-phase-shift algorithm provides a practical solution to center-aligned PWM asymmetry in DMA burst mode applications. This technique enables efficient motor control implementation while maintaining the timing precision required for professional motor drive applications.

### 7.1 Key Contributions

1. Identification of center-aligned PWM timing issues in DMA applications
2. Development of split-and-phase-shift algorithm for phase alignment
3. Practical implementation for 2-phase motor control
4. Verification methodology using standard test equipment

### 7.2 Future Applications

The algorithm can be extended to:
- 3-phase motor control systems
- Higher resolution lookup tables
- Other STM32 timer configurations
- Variable frequency drive applications

## 8. Implementation Guidelines

### 8.1 Step-by-Step Implementation

1. **Generate base lookup table**: Create sinusoidal values for desired motor control
2. **Apply split algorithm**: Convert each triplet to two half-triplets
3. **Implement phase offset**: Move first half-triplet to array end
4. **Configure DMA burst mode**: Set up timer for 3-register burst transfer
5. **Verify with test equipment**: Confirm elimination of concatenation artifacts

### 8.2 Design Considerations

- Array size doubles from original implementation
- Memory usage increases but remains reasonable for most applications
- Algorithm preserves original sinusoidal characteristics
- Compatible with existing STM32 development tools

## Acknowledgments

This work was developed with assistance from GitHub Copilot, demonstrating the potential for AI-assisted embedded systems development. The solution was implemented and verified on STM32G071 hardware using standard development tools.

## References

- STM32G0xx Reference Manual (RM0444)
- X-CUBE-MCSDK Motor Control Software Development Kit
- STM32 Timer Cookbook Application Notes
- Center-Aligned PWM Mode Documentation

---

1 REPLY 1
FMadi.1
Associate III

I'm providing a demonstration project.  README explains how to use it.

 
 
# STM32G071 2-Phase 3-Wire PWM with DMA

A minimal example demonstrating 2-phase 3-wire PWM generation using STM32G071 with DMA burst mode for motor control applications.

## Overview

This project generates 3-wire PWM signals for 2-phase motor control:
- **Phase A**: TIM1_CH1 - TIM1_CH3 (PA8 - PA10)
- **Phase B**: TIM1_CH2 - TIM1_CH3 (PA9 - PA10)
- **90° phase relationship** between Phase A and Phase B
- **3-wire configuration** with common neutral (CH3)
- **DMA-driven lookup table** (384 elements) for smooth sinusoidal control
- **Center-aligned PWM** at 3840Hz (60Hz × 64 pulses)
- **LED indicator** on PA5 for system health monitoring

## Hardware Requirements

- **STM32G071RB NUCLEO board** (or compatible STM32G071 hardware)
- **ST-LINK programmer** (integrated on NUCLEO)
- **PWM output pins**:
  - PA8: TIM1_CH1 (Phase A)
  - PA9: TIM1_CH2 (Phase B)
  - PA10: TIM1_CH3 (Common/Neutral)
  - PA5: LED_GREEN (status indicator)

## Software Requirements

- **STM32CubeMX** (version 6.15.0 or later)
- **ARM GCC toolchain** (arm-none-eabi-gcc)
- **STM32 ST-LINK Utility** or **st-flash** tool
- **Make** utility

## Project Generation from 4 Files

This project can be fully regenerated from just 4 essential files:

### Required Files:
1. **`G071_DMA_PWM.ioc`** - STM32CubeMX configuration
2. **`main.c`** - Application code with PWM algorithm  
3. **`Makefile`** - Build system configuration
4. **`README.md`** - This documentation file

### Generation Steps:

1. **Create Project Directory**
   ```bash
   mkdir G071_DMA_PWM
   cd G071_DMA_PWM
   # Place all 4 files in this directory
   ```

2. **Open STM32CubeMX**
   ```bash
   # Open the G071_DMA_PWM.ioc file in STM32CubeMX
   ```

3. **Generate Code**
   - Click "Generate Code" button
   - Select "Copy all used libraries into the project folder"
   - Set "Project Location" to current directory
   - Generate (creates Core/, Drivers/ folders and other files)

4. **Replace Generated main.c**
   ```bash
   # CubeMX creates Core/Src/main.c with template code
   # Replace it with the optimized version:
   cp main.c Core/Src/main.c
   ```

5. **Build Project**
   ```bash
   make clean
   make
   ```

6. **Flash to Hardware**
   ```bash
   make flash
   ```

## Project Configuration Details

### Clock Configuration:
- **System Clock**: 64MHz (HSI 16MHz → PLL ×8 ÷2)
- **TIM1 Clock**: 64MHz
- **Prescaler**: 4 (5-1) (effective 12.8MHz timer clock)

### TIM1 Configuration:
- **Mode**: Center-aligned counter mode 1
- **Frequency**: 3840Hz PWM (for 60Hz motor control)
- **Channels**: 3 PWM channels (CH1, CH2, CH3)
- **DMA**: Burst mode updating CCR1, CCR2, CCR3 registers

### DMA Configuration:
- **DMA1 Channel 1** → TIM1_UP
- **Transfer**: Memory to Peripheral, 16-bit halfword
- **Mode**: Circular, 384 elements
- **Source**: Sinusoidal lookup table

## Build System

The included `Makefile` provides:

```bash
make           # Build project
make clean     # Clean build files  
make flash     # Program via st-flash
make program   # Alternative programming command
make erase     # Erase flash memory
make reset     # Reset target MCU
```

### Build Requirements:
- ARM GCC toolchain in PATH
- st-flash utility for programming
- Standard make utility

## Algorithm Details

### 2-Phase 3-Wire PWM:
The algorithm generates two sinusoidal phases with 90° offset:
- **384-element lookup table** with pre-calculated values
- **Phase A = CH1 - CH3** (PA8 - PA10)
- **Phase B = CH2 - CH3** (PA9 - PA10)
- **DMA automatically cycles** through lookup table

### Key Features:
- **No CPU overhead** during PWM generation (DMA-driven)
- **Precise timing** with center-aligned PWM
- **Smooth sinusoidal output** from lookup table
- **Minimal code footprint** for educational purposes

## Usage

1. **Connect oscilloscope** to PA8, PA9, PA10 to observe PWM signals
2. **Power on NUCLEO board** via USB
3. **Flash the firmware**:
   ```bash
   make flash
   ```
4. **Observe LED blinking** on PA5 (indicates system running)
5. **Measure PWM outputs**:
   - PA8-PA10: Phase A signal (relative to common)
   - PA9-PA10: Phase B signal (relative to common)
   - 90° phase relationship between A and B

## Troubleshooting

### Build Issues:
- Ensure ARM GCC toolchain is installed and in PATH
- Verify st-flash utility is available
- Check file permissions in project directory

### Hardware Issues:
- LED not blinking: Check NUCLEO power and ST-LINK connection
- No PWM output: Verify pin connections and scope probes
- Upload fails: Try `make erase` then `make flash`

### CubeMX Regeneration:
- The optimized `main.c` contains custom code throughout (not just in USER CODE sections)
- When replacing CubeMX-generated `main.c`, you get the complete optimized version
- If you need to modify CubeMX settings later, regenerate and re-copy the optimized `main.c`
- Some regenerated code may include error checking that can be removed for minimal footprint

## File Structure After Generation

```
G071_DMA_PWM/
├── README.md                    # Documentation (original)
├── main.c                       # Optimized main.c (original, copied to Core/Src/)
├── G071_DMA_PWM.ioc            # CubeMX configuration (original)
├── Makefile                    # Build system (original)
├── STM32G071XX_FLASH.ld        # Linker script (generated)
├── startup_stm32g071xx.s       # Startup code (generated)
├── Core/
│   ├── Inc/
│   │   ├── main.h              # (generated)
│   │   ├── stm32g0xx_hal_conf.h # (generated)
│   │   └── stm32g0xx_it.h      # (generated)
│   └── Src/
│       ├── main.c              # Main application (copied from root)
│       ├── stm32g0xx_hal_msp.c # HAL MSP configuration (generated)
│       ├── stm32g0xx_it.c      # Interrupt handlers (generated)
│       ├── system_stm32g0xx.c  # System configuration (generated)
│       ├── syscalls.c          # System call stubs (generated)
│       └── sysmem.c            # Memory management (generated)
└── Drivers/                    # STM32 HAL drivers (auto-generated)
```

**Note**: After generation, you can optionally remove the original `main.c` from the root directory since it's now copied to `Core/Src/main.c`.

## License

This project uses STMicroelectronics HAL libraries and follows their licensing terms. The custom application code is provided as-is for educational and development purposes.

## References


---

**Created for STM32 community forum sharing - Educational example of DMA-driven PWM generation**