Week 04 — Embedded Programming

This week focused on programming a microcontroller and understanding how code can interact with the physical world through LEDs, switches, sensors and motors.

Overview

The assignment this week was to browse the datasheet of a microcontroller and write a program that interacts with input and output devices.

I used the Seeed Studio XIAO RP2040 together with Arduino IDE. The week started with a simple blink test and gradually expanded into experiments using an external LED, push button input, a sound sensor module, a servo motor and a potentiometer.

By building small experiments step by step I was able to understand how a microcontroller reads inputs, processes logic and controls hardware outputs.

Tools Used

Key Outputs


Learning Process

At the beginning of the week I was not familiar with the workflow of the XIAO RP2040. I needed to understand how to install the correct board package in Arduino IDE and how the uploading process works.

After reading documentation and watching tutorials I successfully configured the board and started testing basic programs. From there I gradually added more hardware components and tested them one by one.


Group Assignment

Comparison Report on Embedded Architecture Toolchains and Development Workflows

There was no separate group assignment page available for this week. So I included the group comparison report directly in this page.

1. Introduction

Embedded systems are widely used in IoT, industrial control, automotive electronics, and many other fields. Different embedded architectures have different toolchains and workflows. These include ARM Cortex-M, AVR, RISC-V, and ESP. This report compares several mainstream architectures and helps explain which platform may fit different projects.

2. Selected Embedded Architectures

Our team selected the following mainstream embedded architectures for comparison:

The XIAO RP2040 used in my individual work is also based on an ARM Cortex-M0+ processor. This helped me connect my own board test with the group comparison.

3. Toolchain Comparison

Architecture Compiler Tools IDE / Development Environment Debugging Tools Simulation Tools
ARM Cortex-M GCC (arm-none-eabi-gcc), Keil, IAR STM32CubeIDE, Keil, PlatformIO J-Link, ST-Link QEMU, Keil
AVR AVR-GCC, Atmel Studio Arduino IDE, Atmel Studio, PlatformIO USBasp, AVR Dragon Proteus, SimulIDE
RISC-V GCC (riscv64-unknown-elf-gcc) VS Code + PlatformIO, Freedom Studio OpenOCD, J-Link QEMU
ESP GCC (xtensa-esp32-elf-gcc) Arduino IDE, ESP-IDF, PlatformIO ESP-Prog, JTAG Proteus, Tinkercad

4. Development Workflow Comparison

ARM Cortex-M (STM32)
  1. Install Toolchain: install GCC for ARM, Keil, or STM32CubeIDE.
  2. Write Code: use the STM32 HAL library or bare-metal programming.
  3. Compile: use arm-none-eabi-gcc for compilation.
  4. Flash to Board: use ST-Link to upload the code.
  5. Debug: use GDB or OpenOCD for debugging.
AVR (Arduino)
  1. Install Arduino IDE and the necessary board support package.
  2. Write Code: develop using C or C++.
  3. Compile: use AVR-GCC for compilation.
  4. Flash Program: upload code using USBasp or Arduino Bootloader.
  5. Debug: use serial printing or Proteus for simulation.
RISC-V
  1. Install a RISC-V GCC toolchain.
  2. Set up VS Code, PlatformIO, or Freedom Studio.
  3. Write Code: develop using C or C++.
  4. Compile: use riscv64-unknown-elf-gcc for compilation.
  5. Flash and Debug: use OpenOCD or J-Link.
ESP (ESP32)
  1. Install ESP-IDF or Arduino IDE.
  2. Write Code: develop using C or MicroPython.
  3. Compile: use xtensa-esp32-elf-gcc for compilation.
  4. Flash Program: upload using USB serial.
  5. Debug: use ESP-Prog or JTAG.

5. Conclusion

ARM Cortex-M is suitable for industrial and high-performance embedded applications. It has a mature toolchain, but the learning curve can be steep.

AVR, especially Arduino, is ideal for entry-level projects. It is easy to use, but its performance is limited.

RISC-V is an emerging architecture. It is useful for open-source development, although its toolchain is still evolving.

The ESP series is ideal for IoT and wireless communication projects. It offers a rich ecosystem and a user-friendly development process.

Different architectures are suitable for different project scenarios. Developers should choose the most appropriate platform based on their needs.


Programming Experiments

I approached this week as a sequence of small experiments. Each experiment helped me understand a different part of embedded programming.

What is PWM?

PWM means Pulse Width Modulation. It turns a digital pin on and off very quickly.

By changing the on-time ratio, PWM can create different output effects. I used PWM for servo control.

For servo control, different pulse widths represent different angles. The servo reads these pulses and moves to the target angle.

1. Blink — Built-in LED

Blink example
void setup(){
pinMode(LED_BUILTIN, OUTPUT);
}

void loop(){
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}

Code reference: This test followed the structure of the Arduino Blink example.

I changed it for the XIAO RP2040 and used LED_BUILTIN.

Reference: Arduino Blink example.

ComponentCode PinConnectionWhat the code does
Built-in LED LED_BUILTIN No external wiring was needed. The code turns the built-in LED on and off every second.

2. External LED

External LED control
int led = 3;

void setup(){
pinMode(led, OUTPUT);
}

void loop(){

digitalWrite(led, HIGH);
delay(1000);

digitalWrite(led, LOW);
delay(1000);

}

Code reference: This test used the same basic logic as the Arduino Blink example.

I changed the output pin to match my external LED wiring.

References: Arduino Blink example and Arduino digitalWrite().

3. Button Controlling LED

Button input
const int buttonPin = 28;
const int ledPin = 3;

void setup(){

pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);

}

void loop(){

int buttonState = digitalRead(buttonPin);

if(buttonState == LOW){
digitalWrite(ledPin, HIGH);
}
else{
digitalWrite(ledPin, LOW);
}

}

Code reference: This code was based on Arduino input pull-up logic.

I changed the pins and connected the momentary button to GND.

References: Arduino Input Pullup Serial example and Arduino digitalRead().

I used a momentary push button for this test. It is not a latching switch.

Because I used INPUT_PULLUP, the input pin reads HIGH when the button is not pressed.

When I press the button, the pin connects to GND. Then the pin reads LOW, and the LED turns on.

So the LED only stays on while I keep pressing the button.

ComponentCode PinConnectionWhat the code does
Momentary push button buttonPin = 28 One side → pin 28. Other side → GND. The pin reads LOW only when the button is pressed.
External LED and resistor ledPin = 3 Pin 3 → resistor → LED → GND. The LED turns on when buttonState is LOW.

4. Sound Sensor Toggle LED

Sound module
const int soundPin = 28;
const int ledPin = 3;

bool ledState = false;
bool lastSoundState = HIGH;

unsigned long lastTriggerTime = 0;
const unsigned long debounceTime = 300;

void setup(){

pinMode(ledPin, OUTPUT);
pinMode(soundPin, INPUT);

digitalWrite(ledPin, LOW);

}

void loop(){

bool soundState = digitalRead(soundPin);
unsigned long now = millis();

if(soundState == LOW && lastSoundState == HIGH){

if(now - lastTriggerTime > debounceTime){

ledState = !ledState;

digitalWrite(ledPin, ledState ? HIGH : LOW);

lastTriggerTime = now;

}

}

lastSoundState = soundState;

}

Code reference: I wrote this toggle logic for my sound sensor test.

The code uses Arduino digitalRead() to read the sensor output.

It also uses millis() to add a short debounce time.

References: Arduino digitalRead() and Arduino millis().

ComponentCode PinConnectionWhat the code does
Sound sensor module soundPin = 28 Sensor DO → pin 28. VCC → 3.3V. GND → GND. A sound trigger changes the digital input state.
External LED and resistor ledPin = 3 Pin 3 → resistor → LED → GND. Each valid sound trigger toggles the LED state.

5. Servo Sweep

Servo movement
#include <Servo.h>

Servo myservo;

int pos = 30;

void setup(){

myservo.attach(1);

}

void loop(){

for(pos = 30; pos <= 150; pos++){

myservo.write(pos);
delay(10);

}

for(pos = 150; pos >= 30; pos--){

myservo.write(pos);
delay(10);

}

}

Code reference: This test followed the basic Servo Sweep example.

I changed the movement range to 30°–150° for my servo test.

Reference: Arduino Servo Motor Basics.

ComponentCode PinConnectionWhat the code does
MG90S servo motor myservo.attach(1) Signal → pin 1. VCC → 5V. GND → shared GND. The servo sweeps from 30° to 150° and back.

The servo uses PWM-style control pulses. Different pulse widths move the servo to different angles.

6. Potentiometer Controlling Servo

Analog input
#include <Servo.h>

Servo myservo;

const int potPin = A0;
const int servoPin = 1;

void setup(){

myservo.attach(servoPin);

}

void loop(){

int potValue = analogRead(potPin);

int angle = map(potValue,0,1023,0,180);

myservo.write(angle);

delay(20);

}

Code reference: This test followed the idea of the Arduino Servo Knob example.

I used analogRead() to read the potentiometer.

I used map() to convert the value into a servo angle.

References: Arduino Servo Motor Basics, Arduino analogRead(), and Arduino map().

ComponentCode PinConnectionWhat the code does
Potentiometer potPin = A0 Middle pin → A0. Side pins → 3.3V and GND. The code reads the analog value from the potentiometer.
MG90S servo motor servoPin = 1 Signal → pin 1. VCC → 5V. GND → shared GND. The analog value is mapped to a servo angle.

The potentiometer gives an analog value. The code maps this value from 0–1023 to 0–180 degrees.


Reflection

This week helped me understand how embedded systems connect software and hardware. Simple programs like Blink are useful for verifying that the board works, while sensors and actuators demonstrate how the microcontroller interacts with the environment.

The most interesting part for me was combining sensors and outputs, such as the sound module toggling an LED and the potentiometer controlling a servo motor.


Code Reference and AI Use Statement

Code Reference

The final code was written and tested by myself on the XIAO RP2040. Some code structures followed official Arduino example logic. I changed the pins, wiring, and behavior for my own tests.

Test Main reference How I changed it
Blink and external LED Arduino Blink example I changed the output pin and tested it on the XIAO RP2040.
Button input Arduino Input Pullup Serial example I used a momentary push button and connected it to GND.
Sound sensor toggle Arduino digitalRead() and Arduino millis() I wrote the toggle logic and added debounce timing.
Servo Sweep Arduino Servo Motor Basics I changed the servo angle range to fit my test.
Potentiometer Servo Arduino Servo Knob idea, analogRead(), and map() I connected the potentiometer to A0 and mapped the value to 0–180°.
XIAO RP2040 pinout Seeed Studio XIAO RP2040 documentation I used this to understand pinout, PWM pins, and board setup.

The wiring, uploads, tests, and videos were completed by myself.

AI Use Statement

I used ChatGPT to help improve the documentation text and explain some concepts in simple English.

I also used AI to check whether my button input explanation, PWM explanation, and wiring tables were clear.

I did not use AI to replace the physical testing process. All hardware tests were done by myself.

Prompts Used