A real-time Morse code encoder implementation for the XMC4500 ARM Cortex-M4 microcontroller. This project converts ASCII text strings into visual Morse code signals using precise timing control via the SysTick timer and GPIO-driven LED output.
The system encodes alphanumeric characters (A-Z, 0-9) into their corresponding Morse code representations, outputting dot and dash sequences through an LED with standard Morse timing conventions.
- Overview
- Morse Code Theory
- System Architecture
- Timing Diagrams
- Hardware Requirements
- Software Dependencies
- Project Structure
- Build & Flash
- Debugging
- Configuration
- Technical Details
- Initializes SysTick timer for millisecond-precision timing
- Parses input text strings character by character
- Converts each character to its Morse code equivalent (dots and dashes)
- Controls LED output with precise on/off timing
- Maintains standard Morse timing for symbols, letters, words, and sentences
- Outputs debug information via ITM/SWO for monitoring
- Real-time Morse code generation
- Configurable timing parameters (dot duration, gaps)
- Support for uppercase letters (A-Z) and digits (0-9)
- ITM-based printf debugging over SWO
- Clean modular architecture with separate encoder and alphabet modules
┌─────────────────────────────────────────────────────────────────────────────┐
│ MORSE CODE TIMING RULES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Symbol Duration: │
│ │
│ DOT (.) ████░░░░░░░░░░░░░░░░░░░░░░ (1 unit = 100ms) │
│ DASH (-) ████████████░░░░░░░░░░░░░░ (3 units = 300ms) │
│ │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Gap Duration: │
│ │
│ Intra-symbol gap: ░░░░ (1 unit = 100ms) Between dots/dashes │
│ Letter gap: ░░░░░░░░░░░░ (3 units = 300ms) Between letters │
│ Word gap: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ (7 units = 700ms) │
│ Sentence gap: ░░░░░░░░░░░░░░░░░░░░░░░░... (50 units = 5000ms) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ MORSE ALPHABET EXAMPLES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Letter Morse LED Pattern (█ = ON, ░ = OFF) │
│ ─────── ────── ───────────────────────────────────── │
│ │
│ A .- █░███░░░ │
│ B -... ███░█░█░█░░░ │
│ C -.-. ███░█░███░█░░░ │
│ S ... █░█░█░░░ │
│ O --- ███░███░███░░░ │
│ │
│ 1 .---- █░███░███░███░███░░░ │
│ 2 ..--- █░█░███░███░███░░░ │
│ 0 ----- ███░███░███░███░███░░░ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
| Range | Characters | Count |
|---|---|---|
| Letters | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z | 26 |
| Digits | 0 1 2 3 4 5 6 7 8 9 | 10 |
| Space | (word separator) | 1 |
| Total | 37 |
┌─────────────────────────────────────────────────────────────────────────────┐
│ SYSTEM ARCHITECTURE │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ XMC4500 MCU │
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────────────┐ │
│ │ main.c │ │ MORSE_ENCODER │ │ MORSE_ALPHABET.h │ │
│ │ │ │ │ │ │ │
│ │ • System Init │────►│ • Symbol Send │────►│ • A-Z Lookup Table │ │
│ │ • SysTick Cfg │ │ • Word Convert │ │ • 0-9 Lookup Table │ │
│ │ • Main Loop │ │ • Timing Delay │ │ • ASCII Index Mapping │ │
│ └────────────────┘ └───────┬────────┘ └────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────────────┐ │
│ │ SysTick │ │ GPIO │ │ ITM_IO.c │ │
│ │ Handler │ │ Driver │ │ │ │
│ │ │ │ │ │ • Printf Retarget │ │
│ │ • 1ms Tick │ │ • LED Control │ │ • SWO Output │ │
│ │ • timer_ms++ │ │ • P1.1 Output │ │ • Debug Trace │ │
│ └────────────────┘ └───────┬────────┘ └────────────────────────┘ │
│ │ │
└─────────────────────────────────┼────────────────────────────────────────────┘
│
▼
┌────────────────┐
│ LED (P1.1) │
│ │
│ ● ON = HIGH │
│ ○ OFF = LOW │
└────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ WORD ENCODING TIMING DIAGRAM │
└─────────────────────────────────────────────────────────────────────────────┘
Input: "I CAN MORSE"
I (..)
─────────────────────────────────────────────────────────────────────────────►
│ time
▼
LED: █░█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░...
├─┤ ├─┤
1u 1u
dot dot
├────────────────────┤
WORD GAP (7u = 700ms)
C (-.-.)
LED: ░░░░░░░░░░░░░░░░░░░░░███░█░███░█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░...
├───┤ ├─┤ ├───┤ ├─┤
3u 1u 3u 1u
dash dot dash dot
├────────┤
LETTER GAP (3u)
A (.-)
LED: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█░███░░░░░░░░░░░░░░░░░░░░░░░░...
├─┤ ├───┤
1u 3u
dot dash
├────────┤
LETTER GAP (3u)
... continues for remaining letters
Legend:
█ = LED ON (HIGH)
░ = LED OFF (LOW)
u = unit (100ms base timing)
┌─────────────────────────────────────────────────────────────────────────────┐
│ CHARACTER "C" TIMING BREAKDOWN │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ 0 │ 100 │ 200 │ 300 │ 400 │ 500 │ 600 │ 700 │ 800 │ 900 │1000 │1100 │ ms
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
LED: ████████████░░░░█████░░░░████████████░░░░█████░░░░░░░░░░░░░░░░░░░...
├──────────┤ ├───┤ ├──────────┤ ├───┤ ├──────────┤
DASH GAP DOT GAP DASH GAP DOT LETTER GAP
(300ms) (100ms)(100ms)(100ms)(300ms)(100ms)(100ms) (300ms)
Total duration: 300 + 100 + 100 + 100 + 300 + 100 + 100 + 300 = 1400ms
| Component | Specification |
|---|---|
| MCU | Infineon XMC4500 |
| Core | ARM Cortex-M4 (32-bit) |
| Clock | 120 MHz |
| Flash | 1024 KB |
| RAM | 160 KB |
| Debug | SWD via J-Link |
XMC4500 Microcontroller
┌────────────────────┐
│ │
P1.1 │ ●──────► LED1 │ (Morse Code Output)
│ │
SWD │ ◄═════► J-Link │ (Debug/Programming)
│ │
SWO │ ──────► ITM Trace │ (Printf Debug Output)
│ │
└────────────────────┘
- XMC4500 development board (or compatible)
- Segger J-Link debugger
- LED connected to GPIO P1.1 (or onboard LED)
- USB cable for J-Link connection
| Tool | Version | Purpose |
|---|---|---|
| arm-none-eabi-gcc | 7.0+ | ARM cross-compiler |
| arm-none-eabi-gdb | 7.0+ | Debugger |
| gdb-multiarch | 7.0+ | Alternative debugger |
| make | 4.0+ | Build system |
| JLinkExe | 6.0+ | Flash programming |
| JLinkGDBServer | 6.0+ | GDB debug server |
| JLinkSWOViewerExe | 6.0+ | ITM/SWO trace viewer |
| Library | Version | Purpose |
|---|---|---|
| XMC Peripheral Library | 2.1.16 | Low-level GPIO drivers |
| CMSIS | 4.5+ | ARM Cortex interface |
| Newlib | System | Standard C library |
# Install ARM toolchain
sudo apt-get install gcc-arm-none-eabi gdb-arm-none-eabi gdb-multiarch
# Install J-Link tools (download from Segger website)
# https://www.segger.com/downloads/jlink/
# Set XMC library path
export XMC_LIBDIR=/opt/XMClib/XMC_Peripheral_Library_v2.1.16MORSE/
├── readme.md # This documentation
├── MORSE_TIME/ # Main implementation
│ ├── main.c # Entry point, system initialization
│ ├── MORSE_ENCODER.c # Morse encoding logic, timing control
│ ├── MORSE_ENCODER.h # Encoder function declarations, timing constants
│ ├── MORSE_ALPHABET.h # ASCII to Morse lookup table
│ ├── ITM_IO.c # Printf retargeting via ITM/SWO
│ ├── XMC4500_MACROS_HELP.h # Hardware macro helpers
│ ├── Makefile # Build configuration
│ ├── student.mk # Source file configuration
│ └── GDBStudentCommands # GDB automation commands
├── MORSE_TIME_B/ # Alternative implementation
│ └── ... # Same structure as MORSE_TIME
└── .vscode/ # VS Code configuration
├── settings.json # Project settings
├── tasks.json # Build tasks
├── launch.json # Debug configuration
└── c_cpp_properties.json # IntelliSense configuration
| File | Lines | Purpose |
|---|---|---|
main.c |
~45 | System initialization, SysTick config, main loop |
MORSE_ENCODER.c |
~95 | Core encoding logic, GPIO control, timing delays |
MORSE_ENCODER.h |
~25 | Timing constants, function declarations |
MORSE_ALPHABET.h |
~45 | ASCII-indexed Morse code lookup table |
ITM_IO.c |
~90 | Printf retargeting for GCC, ArmCC, IAR |
# Navigate to project directory
cd MORSE_TIME
# Clean previous build
make clean
# Compile the project
make
# Flash to device via J-Link
make program
# Debug with GDB
make debugarm-none-eabi-gcc -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mthumb ...
Building: main.c
Building: MORSE_ENCODER.c
Building: ITM_IO.c
Linking: build/main.elf
Creating: build/main.hex
text data bss dec hex filename
8192 512 1024 9728 2600 build/main.elf
$ make program
Connecting to J-Link...
Erasing flash...
Programming build/main.hex...
Verifying...
Done.
{
"PROJECT.NAME": "MORSE_TIME"
}{
"version": "0.2.0",
"configurations": [
{
"name": "J-Link (XMC4500)",
"type": "cortex-debug",
"request": "launch",
"servertype": "jlink",
"device": "XMC4500",
"interface": "swd",
"runToEntryPoint": "main",
"cwd": "${workspaceFolder}",
"executable": "${workspaceFolder}/${config:PROJECT.NAME}/build/main.elf",
"gdbPath": "/usr/bin/gdb-multiarch",
"serverpath": "JLinkGDBServer"
}
]
}{
"configurations": [
{
"name": "ARM-GCC",
"includePath": [
"${workspaceFolder}/**",
"/opt/XMClib/XMC_Peripheral_Library_v2.1.16/XMCLib/inc",
"/opt/XMClib/XMC_Peripheral_Library_v2.1.16/CMSIS/Include",
"/opt/XMClib/XMC_Peripheral_Library_v2.1.16/CMSIS/Infineon/XMC4500_series/Include"
],
"defines": ["XMC4500_F100x1024"],
"compilerPath": "/usr/bin/arm-none-eabi-gcc",
"cStandard": "gnu99",
"intelliSenseMode": "linux-gcc-arm"
}
],
"version": 4
}The project uses ITM (Instrumentation Trace Macrocell) to route printf output over SWD/SWO—no extra UART wiring required.
-
Flash your board:
make program
-
Open JLinkSWOViewer:
JLinkSWOViewerExe
-
Configure JLinkSWOViewer:
- Device: XMC4500-1024
- Target Interface: SWD
- CPU Clock: Click MEASURE
- SWO Clock: Click MEASURE
-
Click OK, then Start to view printf output.
- Open
main.cin VS Code - Press F5 to start the Cortex-Debug session
- Use breakpoints, step through code, and inspect variables
# Project name
LD_NAME = main
# Source files
SRCS = main.c MORSE_ENCODER.c ITM_IO.c
# Library sources
LIBSRCS = xmc_gpio.c xmc4_gpio.c
# XMC library path
XMC_LIBDIR = /opt/XMClib/XMC_Peripheral_Library_v2.1.16
# Compiler flags (debug optimized)
SCFLAGS = -std=gnu99 -Og -fno-omit-frame-pointer -fno-inline| Parameter | Value | Description |
|---|---|---|
DOT |
100 ms | Base timing unit |
DASH |
300 ms | 3 × DOT duration |
INTRA_SYMBOL_GAP |
100 ms | Gap between dots/dashes |
LETTER_GAP |
300 ms | Gap between letters |
WORD_GAP |
700 ms | Gap between words |
SENTENCE_GAP |
5000 ms | Gap after sentence |
// Configure SysTick for 1ms interrupts
uint32_t returnCode = SysTick_Config(SystemCoreClock / 1000);
// SystemCoreClock = 120 MHz
// Reload Value = 120,000,000 / 1,000 = 120,000 cycles per tickstatic volatile uint32_t timer_ms = 0;
void SysTick_Handler(void) {
timer_ms++;
}
static void Delay_ms(uint32_t sleep_duration_ms) {
uint32_t now = timer_ms;
while ((timer_ms - now) < sleep_duration_ms);
}// LED ON (transmitting dot/dash)
XMC_GPIO_SetOutputHigh(XMC_GPIO_PORT1, 1);
// LED OFF (gap/pause)
XMC_GPIO_SetOutputLow(XMC_GPIO_PORT1, 1);// ASCII-indexed lookup table for O(1) access
static const char* MORSE_ALPHABET[] = {
[' '] = " ",
['A'] = ".-",
['B'] = "-...",
['C'] = "-.-.",
// ... etc
['0'] = "-----",
['1'] = ".----",
// ... etc
};
// Usage
const char* morse = MORSE_ALPHABET['C']; // Returns "-.-."void ITMInit(void) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // Enable trace
ITM->TER = 1UL; // Enable stimulus port 0
ITM->TCR = (1UL << ITM_TCR_ITMENA_Pos) |
(1UL << ITM_TCR_SYNCENA_Pos) |
(1UL << ITM_TCR_DWTENA_Pos);
TPI->ACPR = 59; // 120 MHz / 60 = 2 MHz SWO
TPI->SPPR = 2; // NRZ encoding
}┌─────────────────────────────────────────┐ 0x1000_0000
│ │
│ FLASH (1 MB) │
│ │
│ .text (code) │
│ .rodata (Morse alphabet table) │
│ │
├─────────────────────────────────────────┤ 0x1FFF_0000
│ │
│ SRAM (160 KB) │
│ │
│ .data (initialized globals) │
│ .bss (timer_ms, etc.) │
│ │
│ Stack │
│ │
└─────────────────────────────────────────┘ 0x2000_0000
| Issue | Solution |
|---|---|
| Debug session won't start | Verify gdbPath and serverpath in launch.json |
| J-Link not detected | Check USB connection, install J-Link drivers |
| No ITM output | Run Edit > Configure > Measure > OK in JLinkSWOViewer |
| Build errors | Ensure XMC_LIBDIR path is correct in student.mk |
| LED not blinking | Verify GPIO P1.1 configuration and LED connection |