# π Toshiba Micromouse Firmware
```
ββββββββββββββββββββββββββββββββββββββββββββ
β βββββββββββ βββββββββββ β
β β LEFT β β RIGHT β β
β β MOTOR β β MOTOR β β
β β T32A0 β β T32A3 β β
β ββββββ¬βββββ ββββββ¬βββββ β
β β β β
β ββββββ΄βββββββββββββββ΄βββββ β
β β TMPM4KNF10AFG β β
β β Cortex-M4 @ 160MHz β β
β β βββββββββββββββββ β β
β β β 1kHz Control β β β
β β β Loop (T32A1) β β β
β β βββββββββββββββββ β β
β ββββββββββββββββββββββββββ β
β β β β
β ββββββ΄βββββ ββββββ΄βββββ β
β β ENC0 β β ENC2 β β
β β Left β β Right β β
β βββββββββββ βββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββ
```
---
## Architecture
Everything is driven by a single 1 kHz control tick. Each tick runs five stages
in order, and each stage consumes the output of the one above it. The maze
planner (`FloodFill`) is pure logic and never touches hardware. `Navigator` owns
all motion and calls the planner for decisions.
`Navigator` runs before `Motion` so the setpoint it produces is acted on within
the same tick rather than one tick late.
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β main.c β
β while(1) { β
β if (Timebase_GetAndClear()) { βββ 1 kHz tick β
β Encoder_Update(); // read encoders, filter speed β
β Odometry_Update(); // pose (x, y, heading) β
β IR_SampleStep(); // advance the IR sampler β
β Navigator_Update(); // plan/turn/drive FSM step β
β Motion_Update(); // PID -> motor PWM (non-blocking)β
β } β
β } β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β once per tick
βββββββββββββ¬ββββββββββββΌββββββββββββ¬βββββββββββββ
βΌ βΌ βΌ βΌ βΌ
ββββββββββ ββββββββββ ββββββββββ βββββββββββββ ββββββββββββ
βEncoder β βOdometryβ β Motion β β Navigator β β FloodFillβ
βspeed + β βpose β βPID + β β FSM: βββ€ BFS β
βpositionβ βx,y,ΞΈ β βmotor β β planβturn β β Algorithmβ
βββββ¬βββββ βββββ¬βββββ βββββ¬βββββ β βdrive β ββββββ¬ββββββ
β β β βββββββ¬ββββββ β
βΌ β βΌ β βΌ
ββββββββββ β βββββββββββ β ββββββββββββ
βENC0/2 β β β Motor β β β IrSensor β
βA-ENC32 β ββββββΊβ TB67H450β ββββββββΊβ ADC β
βquad in β β +T32A0/3β β 4Γ wall β
ββββββββββ β PWM PPG β β sensing β
βββββββββββ ββββββββββββ
```
**Control flow summary**
| Stage | Module | Role |
|-------|--------|------|
| 1 | `Encoder` | Read A-ENC32 counters(CPS) |
| 2 | `Odometry` | Integrate differential-drive pose from encoder deltas |
| 3 | `IrSensor` | Advance one phase of the ambient-cancelling sample cycle |
| 4 | `Navigator` | Non-blocking FSM. Asks `FloodFill` for moves, executes them |
| 5 | `Motion` | Per-wheel PID speed loop β `Motor` PWM duty |
| .. | `Profile` | Trapezoidal velocity profile. Called in `Navigator` |
| .. | `FloodFill` | Pure BFS planner over discovered walls (no hardware) |
---
## Project Structure
```
toshiba-micromouse/
βββ keil/ # Keil Β΅Vision project files
βββ hardware/ # Hardware overview for Mouse
βββ docs/ # Images and documentation assets
βββ README.md # Project landing page
βββ src/
βββ README.md # This file. Firmware architecture
βββ main.c # Entry point, 1 kHz control loop
βββ ModuleTest.C # Testing file
β
βββ drivers/ # Register-level hardware layer
β βββ timer32A.c/h # T32A: 20 kHz PWM (ch0/3) + 1 kHz tick (ch1)
β βββ gpio.c/h # Port configuration + emitter GPIO
β βββ encoder32A.c/h # A-ENC32 quadrature hardware read
β βββ adc.c/h # ADC-I units A & C (IR receivers)
β βββ dma.c/h # DMAC-B burst transfer for ADC (Optional)
β βββ uart.c/h # UART-C serial debug output
β βββ systick.c/h # Blocking Β΅s/ms delay (stateless)
β
βββ modules/ # Application logic layer
βββ Timebase.c/h # 1 kHz tick flag service
βββ Encoder.c/h # Filtered speed + signed position
βββ Odometry.c/h # Differential-drive pose (x, y, ΞΈ)
βββ PID.c/h # Generic PID controller math
βββ Motor.c/h # TB67H450 H-bridge direction + duty
βββ Motion.c/h # PID speed loop bridging EncoderβMotor
βββ Profile.c/h # Trapezoidal velocity profile (mm, mm/s)
βββ IrSensor.c/h # IR sampling, ambient cancel, distance
βββ FloodFill.c/h # BFS flood-fill maze planner
βββ Navigator.c/h # Cell-level motion sequencer (FSM)
```
---
## Hardware Specs
| Component | Part | Interface |
|-----------|------|-----------|
| **MCU** | TMPM4KNF10AFG | Arm Cortex-M4, 160 MHz, 5 V |
| **Motors** | TB67H450AFNG + Pololu #5211 N20 30:1 | T32A PPG PWM @ 20 kHz |
| **Encoders** | A-ENC32 (on-chip) | Quadrature, 12 CPR Γ 29.89 β 359/rev |
| **IR Sensors** | IR LED + phototransistor Γ4 | ADC-I |
| **Debug** | CMSIS-DAP + Level Shifter (TXB0104) | SWD |
### Pin Map
```
βββββββββββββββββββββββββββββββββββββββββββββββ
β PORT A β PA3 βββΊ T32A00OUTA (Left PWM) β
β β PA4 βββΊ T32A00OUTB (Left PWM) β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β PORT C β PC2 βββΊ T32A30OUTA (Right PWM) β
β β PC3 βββΊ T32A30OUTB (Right PWM) β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β PORT N β PN0 βββΊ ENC0A (Left encoder) β
β β PN1 βββΊ ENC0B (Left encoder) β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β PORT D β PD3 βββΊ ENC2A (Right encoder) β
β β PD4 βββΊ ENC2B (Right encoder) β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β PORT L β PL0 βββΊ AINA16 (Far Left IR) β
β β PL1 βββΊ AINA15 (Left IR) β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β PORT J β PJ0 βββΊ AINC00 (Far Right IR) β
β β PJ1 βββΊ AINC01 (Right IR) β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β PORT U β PU0 βββΊ Left IR Emitter β
β β PU1 βββΊ Far Left IR Emitter β
βββββββββββββββββββββββββββββββββββββββββββββββ€
β PORT G β PG4 βββΊ Far Right IR Emitter β
β β PG5 βββΊ Right IR Emitter β
βββββββββββββββββββββββββββββββββββββββββββββββ
```
---
## Timer Configuration
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β T32A0 (Left Motor) β T32A3 (Right Motor) β
β βββββββββββββββββββββ β βββββββββββββββββββββ β
β Mode: 16-bit PPG β Mode: 16-bit PPG β
β Prescaler: 1:1 β Prescaler: 1:1 β
β Frequency: 20 kHz β Frequency: 20 kHz β
β Period: 4000 counts β Period: 4000 counts β
β Pins: PA3, PA4 β Pins: PC2, PC3 β
β Duty: RG0 = 0-4000 β Duty: RG0 = 0-4000 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β T32A1 (Control Loop) β
β βββββββββββββββββββββ β
β Mode: 32-bit Interval β
β Prescaler: 1:1 β
β Frequency: 1 kHz β
β Period: 80000 counts (80 MHz Ξ¦T0) β
β Interrupt: INTT32A01AC (NVIC) β
β Flag: T32A01AC_IRQ_Fire (volatile bool) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
---
## Navigation Model
`FloodFill` and `Navigator` use **relative** moves at their boundary, so only
one of them needs a compass.
- `FloodFill_Plan()` returns one of `FORWARD`, `TURN_LEFT`, `TURN_RIGHT`,
`TURN_AROUND`, `STOP`. It owns the grid cell and heading, and it is the only
module that does.
- `Navigator` holds **no heading**. Every move is a *segment*, a fixed length of
wheel path measured from encoder positions latched at segment start.
- Odometry is not read by `Navigator` at all. It uses `Odometry.h` for the
geometry constants and nothing else.
**Segments**
| Type | Wheels | Length |
|------|--------|--------|
| Drive | both forward | `CELL_SIZE_MM` |
| Turn | opposed | `ΞΈ Γ WHEELBASE_MM / 2` per wheel |
A pivot is an arc, so it is a straight-line profile in disguise. One `Profile`
serves both, which is why there is no separate angular controller.
**FSM**
```
NAV_PLAN β NAV_TURN β NAV_DRIVE β NAV_SETTLE β NAV_PLAN
β
NAV_FINISHED
```
`NAV_TURN` is skipped when the action is `FORWARD`. `NAV_SETTLE` holds the robot
still so the IR filter converges on the cell it stopped in before `NAV_PLAN`
reads walls from it.
**Correction**
Dead reckoning drifts, so the maze walls are the absolute reference.
- Side IR keeps the robot centred in a corridor, damped by encoder skew
- Front IR resets distance error at any cell facing a wall
- Both fall back to encoder-only when no wall is in range
> **Invariant (load-bearing):** `FloodFill`'s direction enum must stay clockwise
> (N=0, E=1, S=2, W=3). `Dir_Right()` is `(dir + 1) & 3`. Renumbering it breaks
> every turn.
---
## Call Graphs
Generated with [Doxygen](https://www.doxygen.nl/) and rendered by
[Graphviz](https://graphviz.org/). Doxygen parses the source comments and emits
the browsable API docs; Graphviz draws the call and caller graphs.
Full documentation, with every function, its callers, and cross-referenced
source, opens at:
**[`docs/doxygen/html/index.html`](../docs/doxygen/html/index.html)**
Regenerate after changing the source:
```
doxygen Doxyfile
```
**`main()`**
**`Navigator_Update()`**
The graph shows every call the function can make, not which state makes it.
The FSM order is in [Navigation Model](#navigation-model).
---
## Build & Flash
```text
IDE: Keil Β΅Vision
Target: TMPM4KNF10AFG
Debug: CMSIS-DAP
Flash: On-chip 512 KB
```
---
## Deployment Calibration
Before real runs, these must be measured/tuned on the actual robot:
| Where | What | Why |
|-------|------|-----|
| `Encoder` | Forward β **increasing** position | Reversed sign turns PID into positive feedback (runaway) |
| `Odometry.h` | `WHEEL_DIAMETER_MM`, `COUNTS_PER_REV` | Sets `MM_PER_COUNT`. Wrong value scales every distance |
| `Odometry.h` | `WHEELBASE_MM` | Sets the arc length of a turn. Wrong value over or under rotates |
| `IrSensor.c` | `ir_cal[]` ADCβdistance points | One table per channel. Wall detection and wall following both read it |
| `Navigator.c` | `IR_SIDE_TARGET_MM` | Side reading when centred. Only used when one wall is visible |
| `Navigator.c` | `IR_FRONT_TARGET_MM` | Front reading at a cell centre. Sets where a drive stops against a wall |
| `PID.h` | Gains for CPS-scaled error | Error is ~thousands (CPS); default `Kp` saturates instantly |
Angle depends on the *ratio* `MM_PER_COUNT / WHEELBASE_MM`, distance on
`MM_PER_COUNT` alone. Change the wheel and the wheelbase has to move with it or
every turn shifts by the same percentage.
Which test measures each value, and whether it has been done, is tracked in
[`docs/evidence/README.md`](../docs/evidence/README.md).
---
## Module Status
| Module | Status | Description |
|--------|--------|-------------|
| `Timebase` | Done | 1 kHz control tick flag |
| `Encoder` | Done | Filtered speed + signed position |
| `Odometry` | Done | Differential-drive pose estimate |
| `PID` | Done | Generic PID controller |
| `Motor` | Done | H-bridge direction + duty |
| `Motion` | Done | PID speed loop (EncoderβMotor) |
| `Profile` | Done | Trapezoidal velocity profile |
| `IrSensor` | Done | IR sampling, ambient cancel, distance |
| `UART` | Done | Serial debug output (TX) |
| `FloodFill` | Done | BFS flood-fill planner |
| `Navigator` | Done | Segment FSM + wall following |
---
## References
- [TMPM4KNF10AFG Product Page](https://toshiba.semicon-storage.com/us/semiconductor/product/microcontrollers/txz4aplus-series/m4k-group.html)
- [RM-T32A-C Timer Reference](https://toshiba.semicon-storage.com/info/RM-T32A-C_en_20241129.pdf)
- [RM-A-ENC32-A Encoder Reference](https://toshiba.semicon-storage.com/info/RM-A-ENC32-A_en_20250221.pdf)
- [RM-ADC-I Reference](https://toshiba.semicon-storage.com/info/RM-ADC-I_en_20251205.pdf)
- [RM-DMAC-B DMA Reference](https://toshiba.semicon-storage.com/info/RM-DMAC-B_en_20241031.pdf)
- [Pololu #5211 Motor Datasheet](https://www.pololu.com/file/0J1487/pololu-micro-metal-gearmotors-rev-6-2.pdf)
- [Doxygen](https://www.doxygen.nl/) by Dimitri van Heesch, GPL-2.0
- [Graphviz](https://graphviz.org/), EPL-1.0
---
**Author:** Kevin Le β’ 2026