ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
main.c File Reference

Three-phase GPT PWM motor demo for EK-RA8D2 (GPT0/1/2 -> U/V/W). More...

#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_board_ek_ra8d2.h"
#include "ra8_boot_entry.h"
#include "ra8_cgc.h"
#include "ra8_err.h"
#include "ra8_gpio_constants.h"
#include "ra8_gpt.h"
#include "ra8_isr.h"
#include "ra8_mstp.h"
#include "ra8_port_constants.h"
#include "ra8_port_utils.h"
#include "ra8_time.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  motor_3phase_config_t : uint32_t {
  k_motor_3phase_baud = 115200U ,
  k_motor_3phase_period = 0x0000FFFFUL ,
  k_motor_3phase_half_period = 0x00007FFFUL ,
  k_motor_3phase_dead_time = 125U ,
  k_motor_3phase_update_ms = 8U ,
  k_motor_3phase_print_period = 100U ,
  k_motor_3phase_revolution_ms = 2000U
}
 Compile-time settings for the three-phase motor demo. More...
enum  motor_3phase_channel_t : uint8_t {
  k_motor_3phase_ch_u = 0U ,
  k_motor_3phase_ch_v = 1U ,
  k_motor_3phase_ch_w = 2U
}
 Channel assignments for U/V/W phases. More...
enum  motor_3phase_sine_t : uint16_t {
  k_motor_3phase_sine_size = 256U ,
  k_motor_3phase_sine_mask = 255U ,
  k_motor_3phase_phase_120 = 85U ,
  k_motor_3phase_phase_240 = 171U
}
 Sine LUT size and step bookkeeping. More...

Functions

static void internal_motor_3phase_panic_halt (void)
 Halt forever in WFI – used as a panic stop on init failure.
static void internal_motor_3phase_build_sine (void)
 Build the sine LUT using a Bhaskara I approximation.
static ra8_err_t internal_motor_3phase_pins_init (void)
 Route the three GTIOC pins.
static ra8_err_t internal_motor_3phase_pin_configure_all (void)
 Configure GTIOR for the three GTIOCnA outputs (active high).
static void internal_motor_3phase_init_clocks_and_led (void)
 Bring CGC + SysTick + LED1 up.
static void internal_motor_3phase_init_console (void)
 Open the J-Link OB console (SCI8 @ 115200 8N1).
static void internal_motor_3phase_init_pwm (void)
 Open the GPT three-phase block, configure pins, prime duty.
static uint32_t internal_motor_3phase_u32_to_dec (uint32_t value, uint8_t *buf)
 Convert a 32-bit unsigned integer into ASCII decimal.
static void internal_motor_3phase_print_duty (uint32_t u_duty, uint32_t v_duty, uint32_t w_duty)
 Print "duty UVW <u> <v> <w>\\r\\n" over SCI8.
static void internal_motor_3phase_advance (uint32_t *out_u, uint32_t *out_v, uint32_t *out_w)
 Advance the U/V/W phase by one update tick.
void main (void)
 Application entry.

Variables

static const ra8_port_pin_t s_motor_3phase_pin_u = ((ra8_port_pin_t)(((uint16_t)(k_ra8_port_4) << 8) | (uint16_t)(k_ra8_pin_8)))
 Placeholder pin identifiers for the three GTIOCnA outputs.
static const ra8_port_pin_t s_motor_3phase_pin_v = ((ra8_port_pin_t)(((uint16_t)(k_ra8_port_4) << 8) | (uint16_t)(k_ra8_pin_9)))
static const ra8_port_pin_t s_motor_3phase_pin_w = ((ra8_port_pin_t)(((uint16_t)(k_ra8_port_4) << 8) | (uint16_t)(k_ra8_pin_10)))
static const uint8_t s_motor_3phase_msg_prefix [] = "duty UVW "
 Diagnostic banner emitted every 100 ms.
static const uint8_t s_motor_3phase_msg_sep [] = " "
static const uint8_t s_motor_3phase_msg_eol [] = "\r\n"
static uint32_t s_motor_3phase_sine [k_motor_3phase_sine_size]
 Compile-time sine table, normalised to 0..period.
static uint16_t s_motor_3phase_step
 Current sine-table phase index (advances each update tick).

Detailed Description

Three-phase GPT PWM motor demo for EK-RA8D2 (GPT0/1/2 -> U/V/W).

Tag
[Ring 6 / APP] {World: S}

Brings the chip up via ra8_cgc_init() (XTAL -> PLL1 -> CPUCLK0 = 1 GHz, PCLKA = 125 MHz, PCLKD = 125 MHz, SCICLK = PLL1R / 4), routes the GPT0 / GPT1 / GPT2 GTIOCnA outputs onto a J-tag header suitable for an external three-phase motor-driver IC, opens the three channels as a phase-synchronized triple via ra8_gpt_three_phase_open, programmes a 1 us dead-time pair on each channel, and runs a 2-second-period sine-table sweep that smoothly rotates the three duty cycles 0..100 %. The duty values are reported every 100 ms over the J-Link OB CDC port (SCI8 @ 115200 8N1).

Sequence:

  1. ra8_cgc_init() – standard FSP-quickstart clock tree.
  2. ra8_time_init(cpuclk0_hz) for the slow update tick.
  3. ra8_pfs_route_peripheral() for the three GTIOC output pins. The GPT PSEL is k_ra8_psel_gpt0 (0x03) for channels 0..3 and k_ra8_psel_gpt1 (0x04) for higher- numbered channels per HUM Ch 20.4.
  4. ra8_board_uart_console_init(115200) for diagnostic output (J-Link OB console: SCI8 @ 115200 8N1, PD02 TXD / PD03 RXD).
  5. ra8_mstp_init() then ra8_gpt_three_phase_open(&cfg) with U = GPT0, V = GPT1, W = GPT2, triangle-wave PWM, prescaler / 1, period = 0xFFFF (~512 us at 125 MHz PCLKD).
  6. ra8_gpt_dead_time_set(ch, 125, 125) per channel for ~1 us dead-time at PCLKD = 125 MHz.
  7. Initial test pattern: 50 % duty on all three phases via ra8_gpt_three_phase_set_duty(period/2, period/2, period/2).
  8. Loop:
    • Tick a 256-sample sine table at 8 kHz update rate so one full electrical revolution lands in 2 seconds (256 samples * 1 ms = 256 ms ... we extend the period via inner step counter).
    • Recompute U/V/W as 0..period scaled by (1 + sin(theta + phase))/2.
    • ra8_gpt_three_phase_set_duty to push the new triple via the GTCCRC/E shadow buffers.
    • Every 100 ms: print "duty UVW <u> <v> <w>\\r\\n" on SCI8.

Pin-mux notes: the J-tag connector pin allocation for an external motor driver IC was not confirmed against the EK-RA8D2 v1 User's Manual at authoring time; the placeholder pin macros in this file (s_motor_3phase_pin_u etc.) need a hardware bring-up pass before live PWM will reach a motor driver. The application still compiles cleanly and exercises ra8_gpt_init, ra8_gpt_three_phase_open, ra8_gpt_pwm_pin_configure, ra8_gpt_dead_time_set, and ra8_gpt_three_phase_set_duty end-to-end.

Architectural ring
[Ring 6 / APP] {World: S} – application-layer code that runs in the Secure world.
Author
Brighton Sikarskie
Date
2026-04-29
Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ motor_3phase_channel_t

enum motor_3phase_channel_t : uint8_t

Channel assignments for U/V/W phases.

GPT0 -> U-phase (GTIOC0A). GPT1 -> V-phase (GTIOC1A). GPT2 -> W-phase (GTIOC2A). The three channels share a common period and are armed by the same GTSTR write inside ra8_gpt_three_phase_open.

Enumerator
k_motor_3phase_ch_u 

Motor 3phase channel u.

k_motor_3phase_ch_v 

Motor 3phase channel v.

k_motor_3phase_ch_w 

Motor 3phase channel w.

Definition at line 115 of file main.c.

◆ motor_3phase_config_t

enum motor_3phase_config_t : uint32_t

Compile-time settings for the three-phase motor demo.

  • period controls the PWM carrier frequency. At PCLKD = 125 MHz with prescaler / 1, period = 0xFFFF gives a carrier of ~1.91 kHz (62500000 / 32768) for triangle-wave PWM.
  • dead_time is in PCLKD ticks. 125 ticks @ 125 MHz = 1 us.
  • update_period_ms is the rate at which the sine-rotation advances. The 2-second mechanical revolution is implemented as 256 update steps spaced 2000 / 256 ~= 8 ms apart.
Enumerator
k_motor_3phase_baud 

Motor 3phase baud.

k_motor_3phase_period 

Motor 3phase period.

k_motor_3phase_half_period 

Motor 3phase half period.

k_motor_3phase_dead_time 

Motor 3phase dead time.

k_motor_3phase_update_ms 

Motor 3phase update ms.

k_motor_3phase_print_period 

Motor 3phase print period.

k_motor_3phase_revolution_ms 

Motor 3phase revolution ms.

Definition at line 96 of file main.c.

◆ motor_3phase_sine_t

enum motor_3phase_sine_t : uint16_t

Sine LUT size and step bookkeeping.

256-entry quarter-mirrored sine table is overkill for this demo's audible carrier, but matches what FSP's motor-control example uses. A single s_sine_step advances on each update tick.

Enumerator
k_motor_3phase_sine_size 

Motor 3phase sine size.

k_motor_3phase_sine_mask 

Motor 3phase sine mask.

k_motor_3phase_phase_120 

256 / 3 ~= 85.3 (truncated).

k_motor_3phase_phase_240 

2 * 256 / 3 ~= 170.6.

Definition at line 129 of file main.c.

Function Documentation

◆ internal_motor_3phase_advance()

void internal_motor_3phase_advance ( uint32_t * out_u,
uint32_t * out_v,
uint32_t * out_w )
static

Advance the U/V/W phase by one update tick.

Reads three table entries separated by approximately 120 degrees, returns their duties, then advances the shared phase modulo 256.

Parameters
[out]out_uPointer that receives the new U-phase compare value.
[out]out_vPointer that receives the new V-phase compare value.
[out]out_wPointer that receives the new W-phase compare value.
Precondition
out_u / out_v / out_w are non-NULL.
The sine table has been initialized and is not concurrently modified.
Postcondition
s_motor_3phase_step advanced by one (mod sine-table size).
*out_u, *out_v, *out_w are valid duty counts in [0, k_motor_3phase_period].
Note
The integer phase offsets intentionally approximate one third and two thirds of the 256-entry table.
Since
0.1.0

Definition at line 581 of file main.c.

References k_motor_3phase_phase_120, k_motor_3phase_phase_240, k_motor_3phase_sine_mask, s_motor_3phase_sine, and s_motor_3phase_step.

Referenced by main().

◆ internal_motor_3phase_build_sine()

void internal_motor_3phase_build_sine ( void )
static

Build the sine LUT using a Bhaskara I approximation.

Computes sin(theta) for theta = 2*pi*i/N using the Bhaskara I formula in fixed-point so we never pull in libm. The output is shifted into [0, period] so the values can be fed directly to ra8_gpt_three_phase_set_duty.

The Bhaskara formula: sin(x) ~= 16 * x * (pi - x) / (5 * pi^2 - 4 * x * (pi - x)) for x in [0, pi].

For x in [pi, 2*pi] we reflect the result negative.

Precondition
Called once at startup before any timer update.
s_motor_3phase_sine is not being read by another context.
Postcondition
s_motor_3phase_sine[i] holds duty counts for the sample at phase 2*pi*i/N.
Every generated duty value is clamped to the configured PWM period.
Note
The approximation is chosen for deterministic integer-only startup, not for precision motor-control applications.
Since
0.1.0

< pi * 65536 ~= 205887.

< 2pi * 65536 ~= 411775.

< Q16 5 pi sq.

< Q16 one.

< Q16 numer k.

< Q16 denom k.

Definition at line 224 of file main.c.

References k_motor_3phase_half_period, k_motor_3phase_period, k_motor_3phase_sine_size, RA8_INTERNAL, and s_motor_3phase_sine.

Referenced by internal_motor_3phase_init_pwm().

◆ internal_motor_3phase_init_clocks_and_led()

void internal_motor_3phase_init_clocks_and_led ( void )
static

Bring CGC + SysTick + LED1 up.

Halts on any fail.

Splits the long boot sequence so each step stays under the NASA Power-of-10 60-line function-size cap.

Precondition
Reset_Handler has copied .data and zeroed .bss.
Peripheral interrupts remain masked during the boot sequence.
Postcondition
On success the CGC, SysTick, pin mux, and LED1 are live.
Halts in WFI on init failure.
Note
Pin routing occurs only after MSTP and time services are available.
Since
0.1.0

Definition at line 364 of file main.c.

References internal_motor_3phase_panic_halt(), internal_motor_3phase_pins_init(), k_ra8_board_led1, k_ra8_clock_id_cpuclk0, k_ra8_ok, ra8_board_led_init(), ra8_cgc_get_clock_hz(), ra8_cgc_init(), RA8_INTERNAL, ra8_mstp_init(), and ra8_time_init().

Referenced by main().

◆ internal_motor_3phase_init_console()

void internal_motor_3phase_init_console ( void )
static

Open the J-Link OB console (SCI8 @ 115200 8N1).

Delegates SCI8 bring-up, PD02 TXD / PD03 RXD routing, and the baud divisor to ra8_board_uart_console_init so the application carries no board-specific console scaffolding.

Precondition
Clocks initialized.
ra8_mstp_init succeeded.
Postcondition
The console SCI8 channel is enabled.
Halts in WFI on init failure.
Note
Diagnostic output is best-effort after initialization succeeds.
Since
0.1.0

Definition at line 406 of file main.c.

References internal_motor_3phase_panic_halt(), k_motor_3phase_baud, k_ra8_ok, ra8_board_uart_console_init(), and RA8_INTERNAL.

Referenced by main().

◆ internal_motor_3phase_init_pwm()

void internal_motor_3phase_init_pwm ( void )
static

Open the GPT three-phase block, configure pins, prime duty.

After ra8_gpt_three_phase_open arms U/V/W with a shared period, the per-pin GTIOR is programmed for active-high output, stop-low, and POEG-fault-Hi-Z, dead-time is set, and the test pattern (50 % duty on every phase) is loaded.

Precondition
ra8_mstp_init succeeded.
Pin mux for the three GTIOC outputs is established.
Postcondition
All three GPT channels are running synchronized.
Halts in WFI on init failure.
Note
The initial 50 percent pattern is loaded only after pin and dead-time configuration have both succeeded.
Since
0.1.0

Definition at line 433 of file main.c.

References internal_motor_3phase_build_sine(), internal_motor_3phase_panic_halt(), internal_motor_3phase_pin_configure_all(), k_motor_3phase_ch_u, k_motor_3phase_ch_v, k_motor_3phase_ch_w, k_motor_3phase_half_period, k_motor_3phase_period, k_ra8_gpt_mode_triangle_pwm, k_ra8_gpt_ps_div_1, k_ra8_ok, ra8_gpt_three_phase_open(), ra8_gpt_three_phase_set_duty(), RA8_INTERNAL, and s_motor_3phase_step.

Referenced by main().

◆ internal_motor_3phase_panic_halt()

void internal_motor_3phase_panic_halt ( void )
static

Halt forever in WFI – used as a panic stop on init failure.

Repeatedly enters the architectural wait state while retaining the failed initialization context for debugger inspection.

Precondition
Called only after a fatal error in boot.
The application has no safe motor-control recovery path remaining.
Postcondition
CPU is parked; only a debugger or external reset wakes it.
No additional PWM or pin configuration is attempted.
Note
Hardware fault containment must already have disabled unsafe drive outputs before this software-only halt is reached.
Since
0.1.0

Definition at line 190 of file main.c.

References RA8_INTERNAL.

Referenced by internal_motor_3phase_init_clocks_and_led(), internal_motor_3phase_init_console(), internal_motor_3phase_init_pwm(), and main().

◆ internal_motor_3phase_pin_configure_all()

ra8_err_t internal_motor_3phase_pin_configure_all ( void )
staticnodiscard

Configure GTIOR for the three GTIOCnA outputs (active high).

Applies one common output policy and dead-time pair to each of the synchronized U/V/W channels, stopping at the first driver error.

Returns
Error code from the first failing call, or k_ra8_ok.
Return values
k_ra8_okAll three outputs and dead-time pairs were configured.
k_ra8_err_invalid_argA channel or timing argument was rejected.
Precondition
ra8_gpt_three_phase_open already returned k_ra8_ok.
IRQs masked.
Postcondition
Each channel's GTIOR.OAE is set so the pin actually drives.
Each channel's polarity is active-high with stop-low.
Note
Partial configuration is not rolled back because the caller halts before enabling normal motor updates.
Since
0.1.0

< Phase count.

Definition at line 318 of file main.c.

References k_motor_3phase_ch_u, k_motor_3phase_ch_v, k_motor_3phase_ch_w, k_motor_3phase_dead_time, k_ra8_gpt_disable_high_z, k_ra8_gpt_pin_a, k_ra8_gpt_pol_active_high, k_ra8_gpt_stop_low, k_ra8_ok, ra8_gpt_dead_time_set(), ra8_gpt_pwm_pin_configure(), and RA8_INTERNAL.

Referenced by internal_motor_3phase_init_pwm().

◆ internal_motor_3phase_pins_init()

ra8_err_t internal_motor_3phase_pins_init ( void )
staticnodiscard

Route the three GTIOC pins.

Claims and routes the U, V, and W pads in order, returning as soon as a routing request fails so no later pad is touched after an error.

Returns
Error code from the first failing route call, or k_ra8_ok.
Return values
k_ra8_okAll pins routed.
k_ra8_err_gpio_invalid_portPort index out of range.
k_ra8_err_gpio_invalid_pinPin index out of range.
k_ra8_err_gpio_conflictPin already claimed.
Precondition
IOPORT module reachable.
Caller is single-threaded init context.
Postcondition
On success the three GTIOC pins are routed.
On failure previously successful routes remain claimed.
Note
Callers treat partial routing as fatal and park until reset.
Since
0.1.0

Definition at line 283 of file main.c.

References k_ra8_ok, k_ra8_psel_gpt0, RA8_INTERNAL, ra8_pfs_route_peripheral(), s_motor_3phase_pin_u, s_motor_3phase_pin_v, and s_motor_3phase_pin_w.

Referenced by internal_motor_3phase_init_clocks_and_led().

◆ internal_motor_3phase_print_duty()

void internal_motor_3phase_print_duty ( uint32_t u_duty,
uint32_t v_duty,
uint32_t w_duty )
static

Print "duty UVW <u> <v> <w>\\r\\n" over SCI8.

Formats each phase count into one reusable bounded buffer and emits the fixed separators with allocation-free BSP console writes.

Parameters
[in]u_dutyU-phase compare value.
[in]v_dutyV-phase compare value.
[in]w_dutyW-phase compare value.
Precondition
SCI8 is initialized.
Each duty argument is within the configured GPT period.
Postcondition
Seven writes have been issued on SCI8.
No heap or dynamic allocations.
Note
Write failures are ignored because diagnostic loss must not perturb the real-time duty update loop.
Since
0.1.0

< Dec buffer.

Definition at line 534 of file main.c.

References internal_motor_3phase_u32_to_dec(), ra8_board_uart_console_write(), s_motor_3phase_msg_eol, s_motor_3phase_msg_prefix, and s_motor_3phase_msg_sep.

Referenced by main().

◆ internal_motor_3phase_u32_to_dec()

uint32_t internal_motor_3phase_u32_to_dec ( uint32_t value,
uint8_t * buf )
static

Convert a 32-bit unsigned integer into ASCII decimal.

Accumulates decimal digits in reverse order in a bounded local array, then copies them most-significant digit first into buf.

Parameters
[in]valueInteger value.
[out]bufCaller buffer, must hold at least 11 bytes.
Returns
Number of bytes written into buf.
Return values
1One byte was written for zero.
2..10The decimal digit count for a nonzero uint32_t value.
Precondition
buf is non-NULL with room for 11 bytes.
The caller tracks the returned length rather than expecting a NUL.
Postcondition
Buffer holds the decimal MSD-first representation.
Bytes after the returned digit sequence remain unchanged.
Note
This formatter is intentionally independent of the C stdio library.
Since
0.1.0

< Ascii zero.

< Radix.

< Maximum digits.

Definition at line 488 of file main.c.

References RA8_INTERNAL.

Referenced by internal_motor_3phase_print_duty().

◆ main()

void main ( void )

Application entry.

The application entry point Reset_Handler hands control to.

Brings up CGC + GPT triple, runs sweep.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has set VTOR, FPU, and priority grouping.
Postcondition
On clean entry the CPU stays in the sweep loop forever.
On any HAL init failure the function halts in WFI.
Since
0.1.0

Definition at line 605 of file main.c.

References internal_motor_3phase_advance(), internal_motor_3phase_init_clocks_and_led(), internal_motor_3phase_init_console(), internal_motor_3phase_init_pwm(), internal_motor_3phase_panic_halt(), internal_motor_3phase_print_duty(), k_motor_3phase_print_period, k_motor_3phase_update_ms, k_ra8_board_led1, k_ra8_ok, ra8_board_led_toggle(), ra8_delay_ms(), ra8_gpt_three_phase_set_duty(), and ra8_isr_globals_enable().

Variable Documentation

◆ s_motor_3phase_msg_eol

const uint8_t s_motor_3phase_msg_eol[] = "\r\n"
static

Definition at line 155 of file main.c.

Referenced by internal_motor_3phase_print_duty().

◆ s_motor_3phase_msg_prefix

const uint8_t s_motor_3phase_msg_prefix[] = "duty UVW "
static

Diagnostic banner emitted every 100 ms.

Definition at line 153 of file main.c.

Referenced by internal_motor_3phase_print_duty().

◆ s_motor_3phase_msg_sep

const uint8_t s_motor_3phase_msg_sep[] = " "
static

Definition at line 154 of file main.c.

Referenced by internal_motor_3phase_print_duty().

◆ s_motor_3phase_pin_u

const ra8_port_pin_t s_motor_3phase_pin_u = ((ra8_port_pin_t)(((uint16_t)(k_ra8_port_4) << 8) | (uint16_t)(k_ra8_pin_8)))
static

Placeholder pin identifiers for the three GTIOCnA outputs.

TODO: confirm against EK-RA8D2 v1 manual.

The exact pad on the J-tag connector for each GTIOCnA depends on the EK-RA8D2 v1 silk-screen layout; until that is verified, point at three adjacent low-port pins so the validator does not collide with the SCI8 / LED pins. Update once the manual table is read.

Definition at line 148 of file main.c.

Referenced by internal_motor_3phase_pins_init().

◆ s_motor_3phase_pin_v

const ra8_port_pin_t s_motor_3phase_pin_v = ((ra8_port_pin_t)(((uint16_t)(k_ra8_port_4) << 8) | (uint16_t)(k_ra8_pin_9)))
static

Definition at line 149 of file main.c.

Referenced by internal_motor_3phase_pins_init().

◆ s_motor_3phase_pin_w

const ra8_port_pin_t s_motor_3phase_pin_w = ((ra8_port_pin_t)(((uint16_t)(k_ra8_port_4) << 8) | (uint16_t)(k_ra8_pin_10)))
static

Definition at line 150 of file main.c.

Referenced by internal_motor_3phase_pins_init().

◆ s_motor_3phase_sine

uint32_t s_motor_3phase_sine[k_motor_3phase_sine_size]
static

Compile-time sine table, normalised to 0..period.

Built up at runtime in motor_3phase_setup_or_halt using a fixed-point Bhaskara approximation so we avoid pulling in a floating-point sin() implementation. The table holds duty counts directly (0..k_motor_3phase_period), with an offset of half-period so the wave swings symmetrically around the 50%-duty centre line.

Definition at line 168 of file main.c.

Referenced by internal_motor_3phase_advance(), and internal_motor_3phase_build_sine().

◆ s_motor_3phase_step

uint16_t s_motor_3phase_step
static

Current sine-table phase index (advances each update tick).

Definition at line 171 of file main.c.

Referenced by internal_motor_3phase_advance(), and internal_motor_3phase_init_pwm().