Build a Serial-Controlled LED Blinker on Renesas RZ Boards Using FSP (UART + GTM)

by sahana_s in Circuits > Electronics

33 Views, 0 Favorites, 0 Comments

Build a Serial-Controlled LED Blinker on Renesas RZ Boards Using FSP (UART + GTM)

Screenshot 2026-10-09 170427.png

In this Instructable, I'll walk you through a bare-metal application built with the Renesas Flexible Software Package (FSP) in e² studio. You'll type a number into a serial terminal, and an LED's blink speed changes instantly, a simple but powerful way to learn interrupt-driven UART handling and timer peripheral control on a real Arm Cortex-M target.

Supplies

rzg2l_update-firmware.png

Hardware:

  1. 1x RZ board (SMARC EVK) + Module Board + Carrier Board
  2. 1x USB Type-C cable (board power)
  3. 1x PMOD LED module
  4. 1x PMOD USBUART module
  5. 1x USB Type-A to micro-USB cable (serial connection to PC)
  6. 1x J-Link Base + J-Link 9-pin Cortex-M Adapter (debugging)

Software:

  1. e² studio v2026-07 or later
  2. RZ Flexible Software Package (FSP) v4.2.0
  3. GCC Arm Embedded Toolchain v13.3.1.arm-13-24 (bundled with FSP installer)
  4. SEGGER J-Link RTT Viewer v9.44
  5. Tera Term (or similar serial terminal app)

Hardware Setup - Boot Mode & Switch Configuration

e3a8e6c4-b36d-432b-b6a7-f6ab23f50ee1.png

Since the original table didn't copy over cleanly, here's the same configuration rewritten as a simple checklist. Set the board to Boot Mode 3 (1.8-V Single/Quad/Octal serial flash memory) using the switches below before powering on:

On the Module Board:

  1. SW1-1 → OFF
  2. SW1-2 → Don't care (leave as-is)

On the Carrier Board:

  1. SW1 → Don't care
  2. SW2 → 2-3
  3. SW3 → 2-3
  4. SW4 → Don't care
  5. SW5 → Don't care
  6. SW6 → Don't care
  7. SW7 → Don't care
  8. SW8 → Don't care
  9. SW11-1 → OFF
  10. SW11-2 → OFF
  11. SW11-3 → OFF
  12. SW11-4 → ON
  13. CN4 → Jumper connects 1-3 AND Jumper connects 2-4 (two separate jumper connections)


E² Studio & FSP Overview

Screenshot 2026-10-08 182245.png

Before wiring anything else, let's set up the software side

  1. e² studio is Renesas's official Eclipse-based IDE, this is where you'll write, build, flash, and debug your code
  2. RZ FSP (Flexible Software Package) is the HAL (Hardware Abstraction Layer) that provides ready-made drivers (like the UART and GTM drivers we're using here) so you don't have to write register-level code yourself.
  3. GNU Arm Toolchain is the actual compiler that turns your C code into a binary the Cortex-M core can run, this is bundled into the FSP installer automatically

Installation Steps

  1. Download e² studio from Renesas's official info page: "e² studio -information for RZ Family | Renesas" . This page also hosts the "Getting Started with e² studio" documentation, read that first if you've never used e² studio before.
  2. Download the RZ FSP package (includes e² studio plugin + toolchain) directly from the GitHub release:
  3. setup_rzfsp_v4_2_0_e2s_v2026-07.exe - https://github.com/renesas/rz-fsp/releases/download/v4.2.0/setup_rzfsp_v4_2_0_e2s_v2026-07.exe
  4. Run the installer and follow the prompts, this installs: e² studio IDE (v2026-07)
  5. RZ FSP v4.2.0
  6. GNU Arm Toolchain v13.3.1.arm-13-24 (arm-none-eabi target)
  7. Once installed, open e² studio and confirm the FSP version shows v4.2.0 under your project's FSP Configuration screen.

Wire It Up

  1. Connect CN6 on the Carrier Board to a 5V power source via USB Type-C.
  2. Connect the J-Link Base to the Module Board's debug connector via JTAG cable (use the J-Link 9-pin Cortex-M Adapter if needed).
  3. Connect PMOD0 pins 7–12 on the Carrier Board to your Pmod LED.
  4. Connect PMOD1 pins 1–6 on the Carrier Board to your Pmod USBUART.
  5. Connect J1 on the Pmod USBUART to your PC via USB Type-A to micro-USB cable.

Understand the Project Flow

Everything starts in hal_entry():

void hal_entry(void)
{
fsp_err_t err = FSP_SUCCESS;

APP_PRINT(BANNER_INFO, EP_VERSION, version.version_id_b.major, ...);
APP_PRINT("\r\nThe project initializes the UART with baud rate of 115200 bps.");
APP_PRINT("\r\nProvide Input ranging from 1 - 2000 to set LED Period\r\n");

/* 1. Set up the GTM timer in periodic mode */
err = gtm_initialize();
...
/* 2. Start the timer so the LED begins blinking at a default rate */
err = gtm_start();
...
/* 3. Set up UART at 115200 bps */
err = uart_initialize();
...
/* 4. Enter the main demo loop, waits for user input forever */
err = uart_ep_demo();
...
}

Order of operations: timer first → UART second → infinite demo loop. Each step traps on error ( APP_ERR_TRAP) instead of continuing in a broken state

How the Timer (GTM) Side Works

Opening and starting the timer:

fsp_err_t gtm_initialize(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_GTM_Open(&g_timer_ctrl, &g_timer_cfg);
if (FSP_SUCCESS != err)
{
APP_PRINT("\r\n** R_GTM_Open API failed **\r\n");
APP_ERR_TRAP(err);
}
return err;
}

fsp_err_t gtm_start(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_GTM_Start(&g_timer_ctrl);
...
return err;
}

Changing the blink speed on the fly:

fsp_err_t set_cycle(uint32_t raw_count)
{
fsp_err_t err = FSP_SUCCESS;
timer_info_t info = {(timer_direction_t)RESET_VALUE, RESET_VALUE, RESET_VALUE};

err = R_GTM_InfoGet(&g_timer_ctrl, &info);
if (FSP_SUCCESS != err)
{
APP_PRINT("\r\n** R_GTM_InfoGet API failed **\r\n");
APP_ERR_TRAP(err);
}
else
{
/* Convert the user's millisecond value into raw timer counts */
g_clock_freq = info.clock_frequency;
g_raw_counts_periodic = (uint32_t)(raw_count * (g_clock_freq / 1000));

/* Apply the new period — takes effect immediately */
err = R_GTM_PeriodSet(&g_timer_ctrl, g_raw_counts_periodic);
...
}
return err;
}

The key formula: raw_count * (clock_frequency / 1000) converts "I want X ms" into "set the counter to Y ticks."


The LED toggle itself happens in a timer interrupt callback:

void periodic_timer_callback(timer_callback_args_t *p_args)
{
(void) p_args;
static volatile bsp_io_level_t led_level = BSP_IO_LEVEL_HIGH;

led_level ^= BSP_IO_LEVEL_HIGH;
fsp_err_t err = R_IOPORT_PinWrite(&g_ioport_ctrl, (bsp_io_port_pin_t) g_bsp_leds.p_leds[0], led_level);
...
}

This fires automatically every time the timer period elapses, just flipping the LED pin. The speed is controlled entirely by set_cycle().

How the UART Side Works

Opening UART:

fsp_err_t uart_initialize(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_SCIF_UART_Open(&g_uart_ctrl, &g_uart_cfg);
...
return err;
}

Capturing keystrokes — the UART callback (interrupt-driven line buffer):

void user_uart_callback(uart_callback_args_t *p_args)
{
g_uart_event = (uint8_t)p_args->event;

if (DATA_LENGTH == g_counter_var)
{
g_counter_var = RESET_VALUE;
}

if (UART_EVENT_RX_CHAR == p_args->event)
{
switch (p_args->data)
{
case LINEFEED_ASCII:
case CARRIAGE_ASCII:
{
g_counter_var = RESET_VALUE;
g_data_received_flag = true;
break;
}
default:
{
g_temp_buffer[g_counter_var++] = (uint8_t) p_args->data;
break;
}
}
}
}

Characters accumulate into g_temp_buffer one at a time; pressing Enter flips g_data_received_flag to hand off to the main loop.


The main loop, validating and applying input:

fsp_err_t uart_ep_demo(void)
{
fsp_err_t err = FSP_SUCCESS;
volatile bool b_valid_data = true;

while (true)
{
if (g_data_received_flag)
{
g_data_received_flag = false;
uint8_t input_length = (uint8_t)(strlen((char *) &g_temp_buffer));
volatile uint32_t cycle = RESET_VALUE;

if (DATA_LENGTH > (uint8_t)input_length)
{
/* Reject anything that isn't a plain digit */
for (uint8_t buf_index = RESET_VALUE; buf_index < input_length; buf_index++)
{
if (ZERO_ASCII <= g_temp_buffer[buf_index] && NINE_ASCII >= g_temp_buffer[buf_index])
{
b_valid_data = true;
}
else
{
memset(g_temp_buffer, RESET_VALUE, DATA_LENGTH);
b_valid_data = false;
break;
}
}

cycle = ((uint32_t)(atoi((char *) &g_temp_buffer)));

/* Enforce the documented 1–2000 range */
if ((MAX_CYCLE < cycle) || (RESET_VALUE == cycle))
{
memset(g_temp_buffer, RESET_VALUE, DATA_LENGTH);
b_valid_data = false;
uart_print_user_msg((uint8_t *)"\r\nInvalid input. Input range is from 1 - 2000\r\n");
}
}
else
{
memset(g_temp_buffer, RESET_VALUE, DATA_LENGTH);
b_valid_data = false;
uart_print_user_msg((uint8_t *)"\r\nInvalid input. Input range is from 1 - 2000\r\n");
}

if (b_valid_data)
{
err = set_cycle(cycle); /* ← LED speed actually changes here */
memset(g_temp_buffer, RESET_VALUE, DATA_LENGTH);
b_valid_data = false;
uart_print_user_msg((uint8_t *)"\r\nAccepted value, the led is blinking with that value\r\n");
uart_print_user_msg((uint8_t *)"Please set the next value\r\n");
}
}
}
}

Validation pipeline: length check → digit-only check → range check (1–2000) → set_cycle().

Printing feedback back to the terminal (blocking send with timeout guard):

fsp_err_t uart_print_user_msg(uint8_t *p_msg)
{
fsp_err_t err = FSP_SUCCESS;
uint64_t local_timeout = (DATA_LENGTH * UINT32_MAX);
char *p_temp_ptr = (char *)p_msg;
uint8_t msg_len = (uint8_t)(strlen(p_temp_ptr));

g_uart_event = RESET_VALUE;
err = R_SCIF_UART_Write(&g_uart_ctrl, p_msg, msg_len);
if (FSP_SUCCESS != err) { return err; }

while ((UART_EVENT_TX_COMPLETE != g_uart_event) && (--local_timeout))
{
if (UART_ERROR_EVENTS == g_uart_event)
{
return FSP_ERR_TRANSFER_ABORTED;
}
}
if (RESET_VALUE == local_timeout) { err = FSP_ERR_TIMEOUT; }
return err;
}

Build the Project in E² Studio

Open e² studio v2026-07.

Confirm FSP version 4.2.0 is selected in the project's FSP Configuration tab.

Build via Project → Build Project.

Connect the J-Link, and locate the RTT block address for your build:

  1. For the pre-built hex committed to the repo: 0x601105b8
  2. If you modified and rebuilt the project, find the new _SEGGER_RTT address in the generated .map file instead.

Flash and Run

Screenshot 2026-10-08 182204.png
  1. Click the debug button in e² studio to flash via J-Link. If a "Confirm Perspective Switch" dialog appears, click Switch.
  2. Program will stop at Warm_Reset_S, click the resume/run button again, then again at main(), to let the program run freely
  3. Open Tera Term, connect to the Pmod USBUART's COM port at 115200 bps, and enable local echo.
  4. Open J-Link RTT Viewer separately, the banner, instructions, and error messages print here (not Tera Term!).
  5. In Tera Term, type a number between 1 and 2000 and press Enter.

Expected RTT Viewer output on boot:

<EP banner with FSP version>
The project initializes the UART with baud rate of 115200 bps.
Open Serial Terminal with this baud rate value and
Provide Input ranging from 1 - 2000 to set LED Period

Expected Tera Term feedback after a valid entry:

Accepted value, the led is blinking with that value
Please set the next value

Try 50 for a fast blink or 1500 for a slow one, the difference is very visible.


Troubleshooting

1) No RTT output at all - This is usually caused by a wrong _SEGGER_RTT block address. If you rebuilt the project yourself, recheck the generated .map file for the correct address instead of using the default one.

2) LED doesn't change speed - Double-check your PMOD0 wiring (pins 7–12 should connect to the Pmod LED). Also confirm that g_bsp_leds.p_leds[0] in your code matches the actual LED pin you configured in the FSP Configurator.

3) "Invalid input" every time - Make sure local echo is turned on in Tera Term, and that you're actually pressing Enter after typing your number — the callback only processes input when it sees a carriage return (\r) or linefeed (\n).

4) Garbage characters in terminal - Confirm Tera Term's serial port settings are correct: 115200 bps, 8 data bits, no parity, 1 stop bit (8-N-1), and no flow control.

5) e² studio won't find FSP v4.2.0 - Reinstall using the exact installer link provided earlier, then confirm under the project's FSP Configuration tab that version 4.2.0 is actually selected.


What You Learned

  1. How to install and configure e² studio + RZ FSP for bare-metal development
  2. How to structure an FSP application with timer + UART peripherals working together
  3. Interrupt-driven UART reception using a line-buffer pattern
  4. Converting user-friendly units (milliseconds) into raw hardware timer counts
  5. Defensive input validation before acting on serial input
  6. Using J-Link RTT Viewer alongside a serial terminal for debugging


Next Steps

  1. Add a second LED and let the user pick which one to control
  2. Support negative/hex input instead of digit-only validation
  3. Swap GTM periodic mode for PWM duty-cycle control
  4. Log every accepted value to RTT Viewer with a timestamp