Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9650574
Bổ sung thêm STM32
KienTranCNC May 11, 2026
f2cba8d
Fix lỗi
KienTranCNC May 11, 2026
7203034
debug round 1
KienTranCNC May 15, 2026
9f21978
debug round 2
KienTranCNC May 16, 2026
b8aa308
bug fix round 3
KienTranCNC May 16, 2026
1617349
Changes for CI compilations
KienTranCNC Jun 8, 2026
d64114f
remove trash
KienTranCNC Jun 8, 2026
b4e9eca
Update platformio.ini
KienTranCNC Jun 9, 2026
5331e6d
Update build_matrix.yaml
KienTranCNC Jun 9, 2026
999fdae
fix: regenerate platformio.ini to resolve merge conflict (STM32 TICKS…
KienTranCNC Jun 10, 2026
f8d9d03
fix: Phase 2 STM32 core architecture fixes
KienTranCNC Jun 10, 2026
5f864e9
fix for CI compile
KienTranCNC Jun 10, 2026
2c8d4b6
fix CI compile error
KienTranCNC Jun 11, 2026
3f80473
local compile tested
KienTranCNC Jun 12, 2026
dfa36d7
Bổ sung thêm STM32
KienTranCNC May 11, 2026
84c74ca
Fix lỗi
KienTranCNC May 11, 2026
dd3ea23
debug round 1
KienTranCNC May 15, 2026
c6eebe6
debug round 2
KienTranCNC May 16, 2026
7daffd5
bug fix round 3
KienTranCNC May 16, 2026
1555901
Changes for CI compilations
KienTranCNC Jun 8, 2026
e80e310
remove trash
KienTranCNC Jun 8, 2026
f528b65
fix: regenerate platformio.ini to resolve merge conflict (STM32 TICKS…
KienTranCNC Jun 10, 2026
16c7290
fix: Phase 2 STM32 core architecture fixes
KienTranCNC Jun 10, 2026
8cffa84
fix for CI compile
KienTranCNC Jun 10, 2026
d0ebec7
fix CI compile error
KienTranCNC Jun 11, 2026
3fdb394
local compile tested
KienTranCNC Jun 12, 2026
1e0d945
nucleo_g070rb (G0) previously FAIL because it used TIM2 (not exposed …
Tyuyt3975 Jun 12, 2026
9da2274
Merge branch 'master' of https://github.com/Tyuyt3975/FastAccelStepper
Tyuyt3975 Jun 12, 2026
f70aa63
ci: trigger build check
Tyuyt3975 Jun 12, 2026
c666beb
some change in framework arduino stm32 (redefined macro)
Tyuyt3975 Jun 13, 2026
ef020d2
Merge branch 'master' of https://github.com/Tyuyt3975/FastAccelStepper
Tyuyt3975 Jun 13, 2026
873bf69
ci: trigger build check
Tyuyt3975 Jun 13, 2026
6f42a08
feat(stm32): comprehensive STM32 backend support, ISR hardening, and …
Tyuyt3975 Jun 13, 2026
f05c05f
Merge branch 'master' of https://github.com/Tyuyt3975/FastAccelStepper
Tyuyt3975 Jun 13, 2026
e404084
H5xx: dedicated `getTimClock()` branch with `RCC->CFGR2`
Tyuyt3975 Jun 14, 2026
0e15961
Update CHANGELOG.md
Tyuyt3975 Jun 14, 2026
22afa24
Phase 1: Fix Compile Blockers
Tyuyt3975 Jun 15, 2026
f25cf5c
Removed ignore on examples/ to allow neccesary .h files
Tyuyt3975 Jun 15, 2026
e57991c
adds STM32 support to all compile-blocking files
Tyuyt3975 Jun 15, 2026
4fa1a46
adds STM32 pin support to 9 Issue example files
Tyuyt3975 Jun 16, 2026
41bbf5a
apply symmetric guard to all issues .ino
Tyuyt3975 Jun 16, 2026
6d5b1cf
add STM32 support for examples
Tyuyt3975 Jun 16, 2026
dc30183
add stm32 into complex examples
Tyuyt3975 Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/build_arduino_examples_matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ on:
pull_request:
branches: [ master ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
strategy:
Expand Down Expand Up @@ -80,6 +76,11 @@ jobs:
- atmelsam
- rpipico
- rpipico2
- bluepill_f103c8
- nucleo_g070rb
- blackpill_f401cc
- nucleo_h743zi
- nucleo_l476rg

runs-on: ubuntu-latest

Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/build_idf_examples_matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ on:
pull_request:
branches: [ master ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
strategy:
Expand Down
16 changes: 13 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@
.pio
.dir
.tested
.github
.cursor
.venv
.venv/
venv/
env/
.env/
run_avr
pio_dirs
pio_espidf
PCB/
examples/*/*.h
examples/*/*.cpp
# 2026.06.15 Removed ignore on examples/ to allow .h files (e.g. StepperPins_stm32.h)
# to be tracked by git and available for CI build via build-pio-dirs.sh
# examples/*/*.h
# examples/*/*.cpp
!examples/*/test_*.cpp
extras/tests/pc_based/test_[0-9][0-9]
extras/tests/pc_based/FastAccelStepper.cpp
Expand All @@ -32,7 +41,8 @@ extras/tests/simavr_based/simavr
extras/tests/simavr_based/test_sd*/Makefile
extras/tests/simavr_based/test_seq*/Makefile
extras/tests/esp32_hw_based/*.log
examples
# 2026.06.15 Removed ignore on examples/ to allow .h files
# examples
extras/gen_log2_const/Log2Representation.cpp
extras/gen_log2_const/main
extras/tests/simavr_based/.fas
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
1.2.5:
1.2.5:
- ESP32: Enable MCPWM/PCNT driver for IDF 5.3+ on ESP32-S3, ESP32-C6, ESP32-H2
- Add driverType()/driverTypeString() API to query ESP32 driver at runtime
- StepperDemo: guard against selecting unconnected stepper motors
Expand Down
151 changes: 151 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,157 @@ Found on youtube:
As mentioned by kthod861 in [Issue #110](https://github.com/gin66/FastAccelStepper/issues/110):
* [22 01 2021 Stepper POC3](https://youtu.be/fm2_VkUG10k)

## STM32 Arduino Support

FastAccelStepper supports STM32 microcontrollers via the official
[Arduino Core STM32](https://github.com/stm32duino/Arduino_Core_STM32).

### Architecture

- **Step generation**: TIM2 CC interrupt + BSRR/BRR GPIO (push-pull OUTPUT)
- **Steppers**: Up to 4 (any GPIO pins, dynamic slot allocation)
- **Pulse width**: 6 µs (configurable via `STEP_PULSE_WIDTH_US`)
- **Cyclic fill**: PendSV exception, triggered from FAS_TIMER ISR (TIM2 or TIM3 on C0) every 3ms via uwTick
- **GPIO mode**: Standard push-pull OUTPUT (any GPIO pin works)
- **Direction**: Atomic BSRR set/reset — not ODR XOR (race-free)
- **Direction settling**: `_dir_delay_active` state machine, 30µs delay
- **Timer clock**: Auto-detection of TIM2 (or TIM3 on C0) with APB1 prescaler ×2 correction
- **Interrupt safety**: PRIMASK save/restore (reentrant)
- **SR handling**: Snapshot → clear all processed at once (rc_w0)
- **PendSV**: `__attribute__((weak))` — FreeRTOS compatible
- **C0 support**: STM32C0 series uses TIM3 instead of TIM2 (C0 has no TIM2).
TIM3 is 16-bit (ARR=0xFFFF). With PSC=2 → timer clock = 16MHz, resulting
minimum speed ≈ 244 steps/s (16,000,000 / 65535).
Prescaler PSC=2 is integer (48/16=3) → `fas_stm32_clock_error` = 0.

### Default Pin Mapping

| Stepper | Default Pin | Notes |
|---------|-------------|-------|
| 0 | PA0 | Any GPIO pin can be used |
| 1 | PA1 | (PA0-PA3 are conventional only) |
| 2 | PA2 | |
| 3 | PA3 | |

### ⚠ Warnings

1. **FreeRTOS compatibility** — FastAccelStepper uses `PendSV_Handler` (via `__attribute__((weak))`) for deferred queue filling. FreeRTOS may also claim PendSV for task scheduling. If both claim PendSV without coordination, a runtime crash will occur.
- **If you use FreeRTOS**: Add `-DDISABLE_FAS_PENDSV` to build flags, and call `engine->manageSteppers()` periodically from a low-priority task or timer.
- Doing nothing (using both PendSV handlers) will cause undefined behavior.
2. **FAS_TIMER is reserved** — Do not use TIM2 (or TIM3 on STM32C0) elsewhere.
3. **HAL timebase must be SysTick** (default). uwTick is used for cyclic fill.
4. **Do not call HAL_Delay() with steppers running** — TIM2 priority 0 may preempt SysTick. Use millis() polling.
5. **TICKS_PER_S must match the timer counter clock** — It MUST be defined in your
build environment (DO NOT define it in the sketch — separate compilation prevents
`#define` in `.ino` from affecting library `.cpp` files):
- **PlatformIO**: Add to `platformio.ini`:
```ini
build_flags = -DTICKS_PER_S=18000000UL
```
- **Arduino IDE**: Create `build_opt.h` in the sketch folder:
Sketch → Show Sketch Folder → create text file named `build_opt.h` containing:
```cpp
-DTICKS_PER_S=18000000UL
```
⚠️ **The `-D` prefix is required** (not a C `#define` — the file is parsed by the
compiler's command-line argument parser). File must end with a newline.
- TIM2 is used on most STM32 families (TIM3 on STM32C0).
- Use prescaled value (actual timer clock ÷ PSC+1), typically 16-20MHz.
See `pd_stm32/pd_config.h` for examples for each board and the table below.
6. **Clock error**: After `engine.init()`, check `fas_stm32_clock_error`:
if non-zero, `TICKS_PER_S` exceeds actual timer clock.

7. **Error codes**: After `engine.init()`, check `fas_stm32_clock_error`:
- `0` = OK (timer configured correctly)
- `1` = TICKS_PER_S > actual timer clock, or prescaler clamped at 65535
(timing will be wrong — define correct TICKS_PER_S in build_flags)
- `2` = Non-integer prescaler (timer clock not divisible by TICKS_PER_S)
See `pd_stm32/stm32_queue.cpp` for details.
Check via serial monitor (must call `Serial.begin()` before `engine.init()`).

8. **Hardware testing pending** — This STM32 port has been verified by CI compilation
(5 boards: F103, G070, F401, H743, L476) but has **NOT** been tested on physical hardware
with actual stepper motors. Clock calculations and prescalers are mathematically verified
(see `pd_stm32/stm32_queue.cpp`), but real-world timing, pulse generation, and
direction settling have not been validated. Use with caution.

### Adding a new STM32 family

To add support for a new STM32 family not yet in the supported list:

1. **Check timer availability**: Consult the MCU reference manual (RM).
Does the MCU have TIM2? Is it 16-bit or 32-bit? If no TIM2 available, which timer
has 4 CC channels (CCR1-CCR4) that can be used instead?

2. **Add an `#elif` branch** in `src/pd_stm32/stm32_queue.cpp`:
```cpp
#elif defined(STM32XXxx)
#define FAS_TIMER TIM2 // or TIM3, etc.
#define FAS_TIMER_IRQn TIM2_IRQn
#define FAS_TIMER_RCC_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE()
// #define FAS_TIM_IS_16BIT // uncomment if timer is 16-bit
#define FAS_TIMER_ARR_MAX 0xFFFFFFFF // or 0xFFFF if 16-bit
```

3. **Update `fas_tim_set_ccr()`** (line ~207): add your family macro to the
16-bit wrap guard condition if the timer is 16-bit.

4. **Update `getTimClock()`** (line ~174): add APB clock detection if your
family uses a different RCC register layout.

5. **Update CCR init** (line ~320): add your family to the hardcoded CCR
pointer selection block if it uses a different timer (e.g. TIM3 instead of TIM2).

6. **Register your family macro** in `src/pd_stm32/pd_config.h` family guard.

### TICKS_PER_S Reference Table

| Board | MCU | Timer | TIM_CLK | TICKS_PER_S (prescaled) | PSC | Timer actual | Error |
|--------------------|----------|-------------|-----------|------------------------|-----|-------------|-------|
| Blue Pill | F103C8 | TIM2 **16-bit** | 72 MHz | 18000000 | 3 | 18.000 MHz | 0 ✅ |
| Black Pill V2 | F401CC | TIM2 32-bit | 84 MHz | 16800000 | 4 | 16.800 MHz | 0 ✅ |
| Nucleo-G070RB | G070RB | TIM3 **16-bit** | 64 MHz | 16000000 (default) | 3 | 16.000 MHz | 0 ✅ |
| Nucleo-H743ZI | H743ZI | TIM2 32-bit | 200 MHz | 20000000 | 9 | 20.000 MHz | 0 ✅ |
| Nucleo-H743ZI | H743ZI | TIM2 32-bit | 200 MHz | 16666666 | 11 | 16.667 MHz | 2 ⚠️ |
| Nucleo-L476RG | L476RG | TIM2 32-bit | 80 MHz | 16000000 (default) | 4 | 16.000 MHz | 0 ✅ |
| Nucleo-C031C6 | C031C6 | TIM3 **16-bit** | 48 MHz | 16000000 (default) | 2 | 16.000 MHz | 0 ✅ |
| Nucleo-F091RC | F091RC | TIM2 32-bit | 48 MHz | 16000000 (default) | 2 | 16.000 MHz | 0 ✅ |
| Nucleo-L073RZ | L073RZ | TIM2 **16-bit** | 32 MHz | 32000000 | 0 | 32.000 MHz | 0 ✅ |



## Local Compile Test Results (STM32)

FastAccelStepper has been compile-tested locally with the following STM32 boards using PlatformIO + Arduino framework.

### ✅ Boards PASS (15/15 Arduino boards)

| # | Board ID | Chip | Flash | RAM | Status |
|----|---------------------------|----------------|---------|--------|--------|
| 1 | `blackpill_f103c8` | STM32F103C8 | 21004 | 2628 | ✅ PASS |
| 2 | `bluepill_f103c8` | STM32F103C8 | 21004 | 2628 | ✅ PASS |
| 3 | `bluepill_f103c8_128k` | STM32F103CB | 21004 | 2628 | ✅ PASS |
| 4 | `genericSTM32F103C8` | STM32F103C8 | 20880 | 2628 | ✅ PASS |
| 5 | `black_f407ve` | STM32F407VE | 23724 | 2712 | ✅ PASS |
| 6 | `black_f407vg` | STM32F407VG | — | — | ✅ PASS |
| 7 | `genericSTM32F407VET6` | STM32F407VE | — | — | ✅ PASS |
| 8 | `disco_f407vg` | STM32F407VG | — | — | ✅ PASS |
| 9 | `blackpill_f411ce` | STM32F411CE | — | — | ✅ PASS |
| 10 | `genericSTM32F411CE` | STM32F411CE | — | — | ✅ PASS |
| 11 | `nucleo_f411re` | STM32F411RE | — | — | ✅ PASS |
| 12 | `blackpill_f401ce` | STM32F401CE | — | — | ✅ PASS |
| 13 | `blackpill_f401cc` | STM32F401CC | — | — | ✅ PASS |
| 14 | `genericSTM32F401CE` | STM32F401CE | — | — | ✅ PASS |
| 15 | `nucleo_f401re` | STM32F401RE | — | — | ✅ PASS |

### ❌ Boards FAIL

| Board ID | Chip | Reason |
|------------------------|---------------|--------------------------------------------------|
| `disco_f411ve` | STM32F411VE | Board does not support Arduino framework (mbed only). Excluded from Arduino test list. |

**Note**: All tests were performed with `toolchain-gccarmnoneeabi 12.3.1` and `framework-arduinoststm32 2.12.0`. See `extras/doc/stm32_compile_report.md` for full details.

## Contribution

- Thanks ixil for pull request (https://github.com/gin66/FastAccelStepper/pull/19) for ATmega2560
Expand Down
139 changes: 139 additions & 0 deletions collect_source_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""
collect_source.py
-----------------
Chạy script này trong thư mục root của project.
Kết quả: [tên thư mục root].txt chứa cây thư mục ASCII + toàn bộ nội dung source code.
"""

import os
import sys

# ── Cấu hình ──────────────────────────────────────────────────────────────────

# Phần mở rộng được coi là source code (thêm/bớt tuỳ ý)
SOURCE_EXTENSIONS = {
".py", ".js", ".ts", ".jsx", ".tsx",
".java", ".kt", ".scala",
".c", ".cpp", ".cc", ".cxx", ".h", ".hpp",
".cs", ".go", ".rs", ".swift",
".rb", ".php", ".lua", ".r",
".html", ".htm", ".css", ".scss", ".sass", ".less",
".sh", ".bash", ".zsh", ".fish", ".ps1", ".bat", ".cmd",
".sql", ".graphql", ".proto",
".json", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf", ".env",
".xml", ".md", ".rst", ".txt",
".dockerfile", ".makefile",
# thêm tuỳ ý...
}

# Tên file / thư mục cần bỏ qua hoàn toàn
IGNORE_NAMES = {
".git", ".svn", ".hg",
"__pycache__", ".mypy_cache", ".pytest_cache",
"node_modules", ".npm", ".yarn",
"venv", ".venv", "env", ".env",
".idea", ".vscode",
"dist", "build", "out", ".next", ".nuxt",
"coverage", ".coverage",
}

# ── Helpers ────────────────────────────────────────────────────────────────────

def should_ignore(name: str) -> bool:
return name in IGNORE_NAMES or name.startswith(".")


def is_source_file(name: str) -> bool:
_, ext = os.path.splitext(name)
return ext.lower() in SOURCE_EXTENSIONS


def build_tree(root: str, prefix: str = "") -> list[str]:
"""Trả về danh sách dòng ASCII tree."""
lines = []
try:
entries = sorted(os.scandir(root), key=lambda e: (not e.is_dir(), e.name.lower()))
except PermissionError:
return lines

entries = [e for e in entries if not should_ignore(e.name)]

for i, entry in enumerate(entries):
connector = "└── " if i == len(entries) - 1 else "├── "
lines.append(f"{prefix}{connector}{entry.name}")
if entry.is_dir():
extension = " " if i == len(entries) - 1 else "│ "
lines.extend(build_tree(entry.path, prefix + extension))
return lines


def collect_files(root: str) -> list[str]:
"""Trả về danh sách đường dẫn tuyệt đối của tất cả source file, theo thứ tự."""
result = []
for dirpath, dirnames, filenames in os.walk(root, topdown=True):
# Lọc thư mục bị ignore (chỉnh sửa in-place để os.walk không đi vào)
dirnames[:] = sorted(
[d for d in dirnames if not should_ignore(d)]
)
for fname in sorted(filenames):
if not should_ignore(fname) and is_source_file(fname):
result.append(os.path.join(dirpath, fname))
return result


def read_file_safe(path: str) -> str:
"""Đọc file, fallback sang latin-1 nếu UTF-8 lỗi."""
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except UnicodeDecodeError:
with open(path, "r", encoding="latin-1") as f:
return f.read()


# ── Main ───────────────────────────────────────────────────────────────────────

def main():
root = os.path.abspath(".")
root_name = os.path.basename(root)
output_path = os.path.join(root, f"{root_name}.txt")

print(f"📁 Root : {root}")
print(f"📄 Output : {output_path}")

source_files = collect_files(root)

# Loại output file và bản thân script khỏi danh sách
script_path = os.path.abspath(__file__)
source_files = [
f for f in source_files
if os.path.abspath(f) != os.path.abspath(output_path)
and os.path.abspath(f) != script_path
]

with open(output_path, "w", encoding="utf-8") as out:
# ── 1. Cây thư mục ──
out.write(f"{root_name}/\n")
tree_lines = build_tree(root)
out.write("\n".join(tree_lines))
out.write("\n\n")
out.write("=" * 60 + "\n\n")

# ── 2. Nội dung từng file ──
for fpath in source_files:
rel_path = os.path.relpath(fpath, root)
fname = os.path.basename(fpath)

out.write(f"📄 FILE: {rel_path}\n")
out.write("-" * 60 + "\n")
out.write(read_file_safe(fpath))
# Đảm bảo luôn xuống dòng trước footer
out.write("\n")
out.write(f"======end of [{fname}]======\n\n")

print(f"✅ Đã ghi {len(source_files)} file vào {output_path}")


if __name__ == "__main__":
main()
Loading
Loading