Week 3

Board Fab, Round 1

Initially, only the FR1 QPAD-xiao’s were available, so I made one of those. The process was painless, except that I tried to cover the castellated pads completely, which I quickly realized was a mistake. After I finished the board, I proceeded to pinch the display with the palm of my hand, and ripped the traces off the board (see below).

qpad_xiao_try_0 qpad_xiao_try_0

Figure 1

I briefly tried repairing it with copper tape, but Quentin pointed out it would be faster to make a new board, so I did, and it was. The castellated board turned out better this time. The display also fared better and survived most of my trip to 023, until it slipped off the lid of my laptop as I was opening the door.

qpad_xiao_try_1 qpad_xiao_try_1

Figure 2

Before I had a chance to make my third board, Quentin lent me one of his for my next experiment.

Remote Pygame

Quentin wrote an embedded program for the QPAD-xiao that allows streaming a buffer in over serial. Using his encoding and based on his demo Python script, I made a simple wrapper. Then, I made an interface for the popular Python library Pygame. The nice thing about this library is that there are many, many examples on the internet of little games written using it.

For instance, somebody made a Maze game. Integrating the QPAD interface was easy:

commit a3b3b8a7627d8dd2f5cc7431c2e34ad82323acb0
Author: Dimitar Dimitrov <dimitar.dimitrov@cba.mit.edu>
Date:   Mon Sep 22 16:02:07 2025 -0400

    use QPAD

diff --git a/main.py b/main.py
index 8ef89a6..d0fb7dd 100644
--- a/main.py
+++ b/main.py
@@ -4,6 +4,8 @@ from player import Player
 from game import Game
 from clock import Clock

+from pygame_qpad import PygameQPAD
+
 pygame.init()
 pygame.font.init()

@@ -55,6 +57,8 @@ class Main():
                player = Player(tile // 3, tile // 3)
                clock = Clock()

+               qpad = PygameQPAD("/dev/ttyACM0", 84.5)
+
                maze.generate_maze()
                clock.start_timer()
                while self.running:
@@ -66,31 +70,11 @@ class Main():
                                        pygame.quit()
                                        sys.exit()

-                       # if keys were pressed still
-                       if event.type == pygame.KEYDOWN:
-                               if not self.game_over:
-                                       if event.key == pygame.K_LEFT:
-                                               player.left_pressed = True
-                                       if event.key == pygame.K_RIGHT:
-                                               player.right_pressed = True
-                                       if event.key == pygame.K_UP:
-                                               player.up_pressed = True
-                                       if event.key == pygame.K_DOWN:
-                                               player.down_pressed = True
-                                       player.check_move(tile, maze.grid_cells, maze.thickness)
-
-                       # if pressed key released
-                       if event.type == pygame.KEYUP:
-                               if not self.game_over:
-                                       if event.key == pygame.K_LEFT:
-                                               player.left_pressed = False
-                                       if event.key == pygame.K_RIGHT:
-                                               player.right_pressed = False
-                                       if event.key == pygame.K_UP:
-                                               player.up_pressed = False
-                                       if event.key == pygame.K_DOWN:
-                                               player.down_pressed = False
-                                       player.check_move(tile, maze.grid_cells, maze.thickness)
+                       player.left_pressed = qpad.button_left
+                       player.right_pressed = qpad.button_right
+                       player.up_pressed = qpad.button_up
+                       player.down_pressed = qpad.button_down
+                       player.check_move(tile, maze.grid_cells, maze.thickness)

                        if game.is_game_over(player):
                                self.game_over = True
@@ -101,6 +85,7 @@ class Main():

                        self._draw(maze, tile, player, game, clock)
                        self.FPS.tick(60)
+                       qpad.update()


 if __name__ == "__main__":

I did also adjust the screen a little bit to look better on the QPAD:

commit 6f5ac0957d1437e535bc5d886d76f66ebecc69a5 (HEAD -> main)
Author: Dimitar Dimitrov <dimitar.dimitrov@cba.mit.edu>
Date:   Mon Sep 22 16:13:52 2025 -0400

    resize screen

diff --git a/game.py b/game.py
index 13815ad..8704072 100644
--- a/game.py
+++ b/game.py
@@ -5,7 +5,7 @@ pygame.font.init()
 class Game:
        def __init__(self, goal_cell, tile):
                self.font = pygame.font.SysFont("impact", 35)
-               self.message_color = pygame.Color("darkorange")
+               self.message_color = pygame.Color("cyan")
                self.goal_cell = goal_cell
                self.tile = tile

@@ -28,4 +28,4 @@ class Game:
                if player.x >= goal_cell_abs_x and player.y >= goal_cell_abs_y:
                        return True
                else:
-                       return False
\ No newline at end of file
+                       return False
diff --git a/main.py b/main.py
index d0fb7dd..df113f9 100644
--- a/main.py
+++ b/main.py
@@ -22,9 +22,9 @@ class Main():
                instructions1 = self.font.render('Use', True, self.message_color)
                instructions2 = self.font.render('Arrow Keys', True, self.message_color)
                instructions3 = self.font.render('to Move', True, self.message_color)
-               self.screen.blit(instructions1,(655,300))
-               self.screen.blit(instructions2,(610,331))
-               self.screen.blit(instructions3,(630,362))
+               self.screen.blit(instructions1,(533,159))
+               self.screen.blit(instructions2,(488,175))
+               self.screen.blit(instructions3,(508,192))

        # draws all configs; maze, player, instructions, and time
        def _draw(self, maze, tile, player, game, clock):
@@ -42,10 +42,10 @@ class Main():
                self.instructions()
                if self.game_over:
                        clock.stop_timer()
-                       self.screen.blit(game.message(),(610,120))
+                       self.screen.blit(game.message(),(488,63))
                else:
                        clock.update_timer()
-               self.screen.blit(clock.display_timer(), (625,200))
+               self.screen.blit(clock.display_timer(), (503,106))

                pygame.display.flip()

@@ -57,13 +57,13 @@ class Main():
                player = Player(tile // 3, tile // 3)
                clock = Clock()

-               qpad = PygameQPAD("/dev/ttyACM0", 84.5)
+               qpad = PygameQPAD("/dev/ttyACM0", 175)

                maze.generate_maze()
                clock.start_timer()
                while self.running:
                        self.screen.fill("gray")
-                       self.screen.fill( pygame.Color("darkslategray"), (603, 0, 752, 752))
+                       self.screen.fill( pygame.Color("darkslategray"), (481, 0, 630, 399))

                        for event in pygame.event.get():
                                if event.type == pygame.QUIT:
@@ -89,8 +89,8 @@ class Main():


 if __name__ == "__main__":
-       window_size = (602, 602)
-       screen = (window_size[0] + 150, window_size[-1])
+       window_size = (480, 320)
+       screen = (window_size[0] + 160, window_size[-1])
        tile_size = 30
        screen = pygame.display.set_mode(screen)
        pygame.display.set_caption("Maze")
diff --git a/player.py b/player.py
index 0a67126..7be72f7 100644
--- a/player.py
+++ b/player.py
@@ -63,4 +63,4 @@ class Player:
                self.x += self.velX
                self.y += self.velY

-               self.rect = pygame.Rect(int(self.x), int(self.y), self.player_size, self.player_size)
\ No newline at end of file
+               self.rect = pygame.Rect(int(self.x), int(self.y), self.player_size, self.player_size)

With these two changes in place, the game worked perfectly:

Note that the game output is mirrored between the QPAD and my laptop’s display, and controlled using the QPAD.

The same author also made a version of Flappy Bird. To integrate the QPAD interface:

commit 9c270f3d1b0e92cbc072532ad1eafaf76422b5fa
Author: Dimitar Dimitrov <dimitar.dimitrov@cba.mit.edu>
Date:   Mon Sep 22 15:50:23 2025 -0400

    use QPAD

diff --git a/game.py b/game.py
index d2d7e62..17f8f13 100644
--- a/game.py
+++ b/game.py
@@ -17,8 +17,8 @@ class GameIndicator:
                self.screen.blit(score, (WIDTH // 2, 50))

        def instructions(self):
-               inst_text1 = "Press SPACE button to Jump,"
-               inst_text2 = "Press \"R\" Button to Restart Game."
+               inst_text1 = "Press SPACE on keyboard or A on QPAD to Jump,"
+               inst_text2 = "Press R on keyboard or B on QPAD to Restart Game."
                ins1 = self.inst_font.render(inst_text1, True, self.inst_color)
                ins2 = self.inst_font.render(inst_text2, True, self.inst_color)
                self.screen.blit(ins1, (95, 400))
diff --git a/main.py b/main.py
index ea83ee5..05bc94b 100644
--- a/main.py
+++ b/main.py
@@ -2,6 +2,8 @@ import pygame, sys
 from settings import WIDTH, HEIGHT
 from world import World

+from pygame_qpad import PygameQPAD
+
 pygame.init()

 screen = pygame.display.set_mode((WIDTH, HEIGHT))
@@ -15,6 +17,8 @@ class Main:
                self.FPS = pygame.time.Clock()

        def main(self):
+               qpad = PygameQPAD("/dev/ttyACM0", 150)
+
                pygame.mixer.music.load("assets/sfx/bgm.wav")
                pygame.mixer.music.play(-1)
                pygame.mixer.music.set_volume(0.8)
@@ -35,10 +39,20 @@ class Main:
                                        if event.key == pygame.K_r:
                                                world.update("restart")

+                       if qpad.state:
+                               if not world.playing and not world.game_over:
+                                       world.playing = True
+                               if qpad.button_a:
+                                       world.update("jump")
+                               if qpad.button_b:
+                                       world.update("restart")
+
                        world.update()
                        pygame.display.update()
                        self.FPS.tick(60)

+                       qpad.update()
+
 if __name__ == "__main__":
        play = Main(screen)
        play.main()

To adjust the screen:

commit 1043388e1b0e0b333c4fa2277213e20125338f47
Author: Dimitar Dimitrov <dimitar.dimitrov@cba.mit.edu>
Date:   Mon Sep 22 16:35:08 2025 -0400

    adjust aspect ratio and font

diff --git a/game.py b/game.py
index 17f8f13..064bdfc 100644
--- a/game.py
+++ b/game.py
@@ -6,9 +6,9 @@ pygame.font.init()
 class GameIndicator:
        def __init__(self, screen):
                self.screen = screen
-               self.font = pygame.font.SysFont('Bauhaus 93', 60)
-               self.inst_font = pygame.font.SysFont('Bauhaus 93', 30)
-               self.color = pygame.Color("white")
+               self.font = pygame.font.SysFont('Bauhaus 93', 120)
+               self.inst_font = pygame.font.SysFont('Bauhaus 93', 60)
+               self.color = pygame.Color("black")
                self.inst_color = pygame.Color("black")

        def show_score(self, int_score):
diff --git a/settings.py b/settings.py
index 52cb46a..4c9177a 100644
--- a/settings.py
+++ b/settings.py
@@ -1,4 +1,4 @@
-WIDTH, HEIGHT = 600, 680
+WIDTH, HEIGHT = 1200, 640

 pipe_pair_sizes = [
        (1, 7),
@@ -22,4 +22,4 @@ def import_sprite(path):
                        full_path = f"{path}/{image}"
                        img_surface = pygame.image.load(full_path).convert_alpha()
                        surface_list.append(img_surface)
-       return surface_list
\ No newline at end of file
+       return surface_list

I also slowed down the fps a bit:

commit 4fce3361c81abf7afdd278ef72280be065d32aef (HEAD -> main)
Author: Dimitar Dimitrov <dimitar.dimitrov@cba.mit.edu>
Date:   Mon Sep 22 16:42:47 2025 -0400

    speed up game but slow down fps

diff --git a/bird.py b/bird.py
index 0d7aec2..714111b 100644
--- a/bird.py
+++ b/bird.py
@@ -7,7 +7,7 @@ class Bird(pygame.sprite.Sprite):
                # bird basic info
                self.frame_index = 0
                self.animation_delay = 3
-               self.jump_move = -8
+               self.jump_move = -16

                # bird animation
                self.bird_img = import_sprite("assets/bird")
@@ -43,4 +43,4 @@ class Bird(pygame.sprite.Sprite):
                if is_jump:
                        self._jump()
                self._animate()
-               # print(self.score)
\ No newline at end of file
+               # print(self.score)
diff --git a/main.py b/main.py
index 05bc94b..4336add 100644
--- a/main.py
+++ b/main.py
@@ -49,7 +49,7 @@ class Main:

                        world.update()
                        pygame.display.update()
-                       self.FPS.tick(60)
+                       self.FPS.tick(15)

                        qpad.update()

diff --git a/world.py b/world.py
index 0671254..067ba83 100644
--- a/world.py
+++ b/world.py
@@ -10,7 +10,7 @@ class World:
                self.screen = screen
                self.world_shift = 0
                self.current_x = 0
-               self.gravity = 0.5
+               self.gravity = 2
                self.current_pipe = None
                self.pipes = pygame.sprite.Group()
                self.player = pygame.sprite.GroupSingle()
@@ -40,7 +40,7 @@ class World:
        # for moving background/obstacle
        def _scroll_x(self):
                if self.playing:
-                       self.world_shift = -6
+                       self.world_shift = -24
                else:
                        self.world_shift = 0

@@ -102,4 +102,4 @@ class World:
                self.player.update(player_event)
                self.player.draw(self.screen)

-               self.game.show_score(self.player.sprite.score)
\ No newline at end of file
+               self.game.show_score(self.player.sprite.score)

With all this in place:

By the way, this was my fancy camera setup for the videos above:

camera camera

Figure 3

Board Fab, Round 2

On Tuesday, the QPAD21’s came in, and I made one as soon as I was done with classes. The fabrication process went smoothly except for the USB micro connector. Once the board was done, I took it home and flashed some example code on it, which worked. Then, I decided to unplug the USB cable and, of course, twisted the connector partly off the board. I happened to have a spare micro connector, so I used my setup at home to replace it.

home_setup home_setup

Figure 4

(The brand name on some of my tools hints at how many years I’ve been bad at soldering for. I think these were part of a set I bought in 2003 to make a CMoy Pocket Amplifier.)

radioshack radioshack

Figure 5

I wasn’t able to remove the solder from one of the mechanical holes, so I bent the lead out of place. And so of course, as soon as I unplugged it the next time…

another_failure another_failure

Figure 6

I managed to get the trace this time as well, and I didn’t have a spare QPAD21 at home. So, instead, I hooked up the 5V directly to the programmer:

programmer programmer

Figure 7

With that, I was back in business:

back_in_business back_in_business

Figure 8

For posterity, I tested uploading code using the Upload Using Programmer option in the Arduino IDE. My settings were as follows:

arduino_ide_settings arduino_ide_settings

Figure 9

This step required access to /dev/hidraw1, which was restricted to root. I side-stepped that problem by running sudo chmod o+rw /dev/hidraw1. I will figure out a udev rule for this in the future.

Embedded Programming

Once I had a working board, I developed a game for it. I was inspired by the DVD screensaver from back in the day. My game consists of an ellipse that bounces around inside the rectangular screen. If the ellipse ever touches two sides at the same time, you win, as is of course the case with the DVD screensaver. Unlike the DVD screensaver, however, you have some amount of control. The arrow keys accelerate the ellipse in the corresponding direction. So, if you press Up while the ellipse is moving up, it will speed up. If you press Up while the ellipse is moving down, however, it will slow down. B resets the game. Below is a video of me playing.

It is very hard though, so I can’t get a video of me winning. Here is a picture instead:

win win

Figure 10

I uploaded the source here. I referenced this and this. Note that I have not included serial output because of my current predicament, but I plan to add that as well.

Microcontroller Datasheet

I browsed through the datasheet for the SAM D21/DA1 family of devices, to which the ATSAMD21E18A-U on the QPDA21 belongs. The file is too large to upload here, but is accessible on Microchip’s website and linked from their product page.

MPLAB® X Integrated Development Environment

The MPLAB X IDE is the “recommended” way to program SAM devices according to Microchip’s website. It seems to have replaced Microchip Studio for AVR® and SAM Devices, which I assume is Atmel Studio post-acquisition. I tried to install MPLAB X IDE. The installer was a 953MiB shell script generated by a program called Makeself. It decompresses and executes an elf that’s embedded in the script itself. Despite my better judgment, I tried running it with elevated privileges out of morbid curiosity. (Un)fortunately, it could not be installed because it requires 4.25GB (my entire root partition is 16GiB). Deselecting features didn’t decrease the required size either.

mplab mplab

Figure 11

Doing it without the Arduino IDE

Many years ago I used to use avrdude to upload my code to AVR devices. I have never worked with the SAM platform, though (nor do I remember exactly how to use avrdude). With ARM, I have used buildroot and yocto, neither of which is relevant for bare metal. So, the next step is to learn how to do it. Instead of doing research on the toolchain, I decided to ask an LLM. I’m trying to start using LLMs and figured this would be a good test. I spoke with ChatGPT (transcript here). In particular, I asked it to explain what the Arduino IDE is doing. ChatGPT led me to find /home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/ (where /home/dsd36/ is my home directory) as the source of truth (presumably generated from Quentin’s repo). More usefully, though, the LLM told me that there is a verbose mode in the Arduino IDE, which was a lot more helpful than the LLM itself. I re-flashed my board and went through the output (attached here). Here is what the Arduino IDE did:

  1. It generated the file dvd.ino.cpp (the command to do that wasn’t in the log, frustratingly). It seems that the only differences from my original dvd.ino were prototypes of the functions, which aren’t actually necessary, and the inclusion of Arduino.h:
0a1,2
> #include <Arduino.h>
> #line 1 "/home/dsd36/Clone/kitchen-sink/dvd/dvd.ino"
58a61,67
> #line 59 "/home/dsd36/Clone/kitchen-sink/dvd/dvd.ino"
> void reset_game();
> #line 67 "/home/dsd36/Clone/kitchen-sink/dvd/dvd.ino"
> void setup();
> #line 78 "/home/dsd36/Clone/kitchen-sink/dvd/dvd.ino"
> void loop();
> #line 59 "/home/dsd36/Clone/kitchen-sink/dvd/dvd.ino"
138a148
>

Easy enough, no need to track the tool down.

  1. It found all the relevant libraries by trying repeatedly to compile. Namely it found:
/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/cores/arduino
/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/variants/Generic_x21E
/home/dsd36/Arduino/libraries/Adafruit_SSD1306
/home/dsd36/Arduino/libraries/Adafruit_GFX_Library
/home/dsd36/Arduino/libraries/Adafruit_BusIO
/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/libraries/Wire
/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/libraries/SPI
/home/dsd36/Arduino/libraries/Adafruit_FreeTouch_Library
  1. It compiled dvd.ino.cpp into dvd.ino.cpp.o using arm-none-eabi-g++:
/home/dsd36/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-g++ \
  -mcpu=cortex-m0plus \
  -mthumb \
  -c \
  -g \
  -Os \
  -w \
  -std=gnu++11 \
  -ffunction-sections \
  -fdata-sections \
  -fsingle-precision-constant \
  -Wdouble-promotion \
  -fno-threadsafe-statics \
  -nostdlib \
  --param max-inline-insns-single=500 \
  -fno-rtti \
  -fno-exceptions \
  -MMD \
  -DF_CPU=48000000L \
  -DARDUINO=10607 \
  -DARDUINO_SAMD_ZERO \
  -DARDUINO_ARCH_SAMD \
  -I/home/dsd36/.arduino15/packages/arduino/tools/CMSIS/4.5.0/CMSIS/Include/ \
  -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/tools/CMSIS-Atmel/1.0.0-mattairtech-2/CMSIS/Device/ATMEL/ \
  -DFLOAT_BOTH_DOUBLES_ONLY \
  -DTIMER_732Hz \
  -DCONFIG_H_DISABLED \
  -DCLOCKCONFIG_INTERNAL_USB \
  -DCDC_ONLY \
  -DONE_UART \
  -DONE_WIRE \
  -DONE_SPI \
  -D__8KB_BOOTLOADER__ \
  -D__SAMD21E18A__ \
  -DUSB_VID=0x16D0 \
  -DUSB_PID=0x4557 \
  -DUSBCON \
  "-DUSB_MANUFACTURER=\"Fab Foundation\"" \
  "-DUSB_PRODUCT=\"x21E\"" \
  -DARM_MATH_CM0PLUS \
  -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/cores/arduino \
  -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/variants/Generic_x21E \
  -I/home/dsd36/Arduino/libraries/Adafruit_SSD1306 \
  -I/home/dsd36/Arduino/libraries/Adafruit_GFX_Library \
  -I/home/dsd36/Arduino/libraries/Adafruit_BusIO \
  -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/libraries/Wire \
  -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/libraries/SPI \
  -I/home/dsd36/Arduino/libraries/Adafruit_FreeTouch_Library \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/sketch/dvd.ino.cpp \
  -o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/sketch/dvd.ino.cpp.o
  1. It linked everything into dvd.ino.elf
/home/dsd36/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-gcc \
  -L/home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923 \
  -Os \
  -Wl,--gc-sections \
  -save-temps \
  -fsingle-precision-constant \
  -Wdouble-promotion \
  -T/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/variants/Generic_x21E/linker_scripts/gcc/8KB_Bootloader/flash_256KB.ld \
  -Wl,-Map,/home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.map \
  --specs=nano.specs \
  --specs=nosys.specs \
  -mcpu=cortex-m0plus \
  -mthumb \
  -Wl,--cref \
  -Wl,--check-sections \
  -Wl,--gc-sections \
  -Wl,--unresolved-symbols=report-all \
  -Wl,--warn-common \
  -Wl,--warn-section-align \
  -o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.elf \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/sketch/dvd.ino.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_SSD1306/Adafruit_SSD1306.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/glcdfont.c.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_GenericDevice.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_I2CDevice.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_SPIDevice.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Wire/Wire.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/SPI/SPI.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_FreeTouch_Library/Adafruit_FreeTouch.cpp.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_FreeTouch_Library/adafruit_ptc.c.o \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/core/variant.cpp.o \
  -Wl,--start-group \
  -L/home/dsd36/.arduino15/packages/arduino/tools/CMSIS/4.5.0/CMSIS/Lib/GCC/ \
  -larm_cortexM0l_math \
  -lm \
  /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/../../cores/8c21b13eecfeab60c306615a611f0f53/core.a \
  -Wl,--end-group

In addition to the compiled sketch and the compiled libraries, it included a variant.cpp.o which seems to come form this file: /home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/variants/Generic_x21E/variant.cpp

  1. It converted the .elf into a .bin and a .hex (the latter missing the .eeprom section):
/home/dsd36/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-objcopy -O binary /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.elf /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.bin
/home/dsd36/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-objcopy -O ihex -R .eeprom /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.elf /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.hex

I don’t see where the .hex gets used.

  1. It dumped all the symbols and the size of the .elf. As far as I can tell this is just for logging purposes.

  2. It programmed (and verified) the chip using edbg:

/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/tools/edbg/0.51.0/edbg -b -t samd21 -pv  -o 0x2000 -f "/home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.bin" -x 10

It’s unclear to me how it knows which port to use.

To make sure I didn’t miss anything, I created a dvd.cpp by prepending #include<Arduino.h> to dvd.ino and ran the following:

/home/dsd36/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-g++ -mcpu=cortex-m0plus -mthumb -c -g -Os -w -std=gnu++11 -ffunction-sections -fdata-sections -fsingle-precision-constant -Wdouble-promotion -fno-threadsafe-statics -nostdlib --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -MMD -DF_CPU=48000000L -DARDUINO=10607 -DARDUINO_SAMD_ZERO -DARDUINO_ARCH_SAMD -I/home/dsd36/.arduino15/packages/arduino/tools/CMSIS/4.5.0/CMSIS/Include/ -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/tools/CMSIS-Atmel/1.0.0-mattairtech-2/CMSIS/Device/ATMEL/ -DFLOAT_BOTH_DOUBLES_ONLY -DTIMER_732Hz -DCONFIG_H_DISABLED -DCLOCKCONFIG_INTERNAL_USB -DCDC_ONLY -DONE_UART -DONE_WIRE -DONE_SPI -D__8KB_BOOTLOADER__ -D__SAMD21E18A__ -DUSB_VID=0x16D0 -DUSB_PID=0x4557 -DUSBCON "-DUSB_MANUFACTURER=\"Fab Foundation\"" "-DUSB_PRODUCT=\"x21E\"" -DARM_MATH_CM0PLUS -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/cores/arduino -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/variants/Generic_x21E -I/home/dsd36/Arduino/libraries/Adafruit_SSD1306 -I/home/dsd36/Arduino/libraries/Adafruit_GFX_Library -I/home/dsd36/Arduino/libraries/Adafruit_BusIO -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/libraries/Wire -I/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/libraries/SPI -I/home/dsd36/Arduino/libraries/Adafruit_FreeTouch_Library dvd.cpp -o dvd.cpp.o
/home/dsd36/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-gcc -L/home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923 -Os -Wl,--gc-sections -save-temps -fsingle-precision-constant -Wdouble-promotion -T/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/hardware/samd/1.12.0/variants/Generic_x21E/linker_scripts/gcc/8KB_Bootloader/flash_256KB.ld -Wl,-Map,/home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/dvd.ino.map --specs=nano.specs --specs=nosys.specs -mcpu=cortex-m0plus -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -o dvd.elf dvd.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_SSD1306/Adafruit_SSD1306.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/Adafruit_GFX.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/Adafruit_GrayOLED.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/Adafruit_SPITFT.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_GFX_Library/glcdfont.c.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_BusIO_Register.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_GenericDevice.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_I2CDevice.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_BusIO/Adafruit_SPIDevice.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Wire/Wire.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/SPI/SPI.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_FreeTouch_Library/Adafruit_FreeTouch.cpp.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/libraries/Adafruit_FreeTouch_Library/adafruit_ptc.c.o /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923/core/variant.cpp.o -Wl,--start-group -L/home/dsd36/.arduino15/packages/arduino/tools/CMSIS/4.5.0/CMSIS/Lib/GCC/ -larm_cortexM0l_math -lm /home/dsd36/.cache/arduino/cores/8c21b13eecfeab60c306615a611f0f53/core.a -Wl,--end-group
/home/dsd36/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-objcopy -O binary dvd.elf dvd.bin
/home/dsd36/.arduino15/packages/Fab_SAM_Arduino/tools/edbg/0.51.0/edbg -b -t samd21 -pv -o 0x2000 -f dvd.bin -x 10

That seemed to work no problem. The next step is to generate everything that the linker grabbed from /home/dsd36/.cache/arduino/sketches/050702DC60B29B2ADB6260598FFBC923, but that’s for another time. As a launching-off point, I moved the cache directory and re-ran. The output is here.

Group Assignment

I worked on the group assignment with Sara. Our work is documented on her website.