De AVR-Laptop is een technisch experiment waarbij een ATmega2560 microcontroller wordt ingezet voor taken die normaal buiten het bereik van 8-bit MCU's liggen. Het resultaat is een compacte laptop in een 3D-geprinte behuizing.
Hardware en Functionaliteit Het systeem is uitgerust met een zelfgemaakt QWERTY-toetsenbord, een monochroom LCD-scherm (128x64), een VS1053 audio-decoder, een microSD-kaartlezer en een RTC. De laptop kan diverse applicaties draaien, waaronder een bestandsbeheerder, muziek- en videospeler, en klassieke games zoals Sokoban en Snake.
Software-architectuur Vanwege de extreme beperking van slechts 8 KB RAM is de software zeer strak georganiseerd:
- FreeRTOS: Wordt gebruikt voor echte multitasking, waardoor gebruikers met Alt-Tab kunnen schakelen tussen actieve taken.
- UI Engine: Een centrale engine beheert widgets (knoppen, invoervelden) en formulieren, waardoor taken onafhankelijk van elkaar kunnen communiceren.
- Video-optimalisatie: Video's worden afgespeeld via een aangepast formaat met temporele dithering om grijstinten te simuleren op het monochrome scherm.
Technische Uitdagingen De maker heeft specifieke oplossingen geïmplementeerd voor CPU-efficiëntie, zoals een Interrupt Service Routine (ISR) om de wachttijden van het display te benutten voor audio-data. Ook het toetsenbord is technisch doorontwikkeld met een 8x8 matrix, diodes tegen ghosting en een toner transfer-methode voor de belettering.
AVR-Laptop: Een 8-bit MCU ver buiten de oorspronkelijke ontwerpdoelen drijven
Systeemoverzicht
Hardware
Het apparaat is gehuisvest in een 3D-geprinte behuizing met afmetingen van 12 × 11 × 3,4 cm. De interne componenten bestaan uit:
- Een zelfgemaakt QWERTY-toetsenbord.
- Een monochroom 128 × 64 LCD-scherm.
- Een VS1053 audio-decoder met luidsprekers.
- Een microSD-kaartlezer.
- Een Real-Time Clock (RTC).
- Een 18650-batterij.
Software en Functionaliteiten
De firmware is gebaseerd op FreeRTOS. De laptop beschikt over de volgende mogelijkheden:
- Multitasking: Echte multitasking met Alt-Tab schakelen. Het is mogelijk om bijvoorbeeld een video te starten en deze te minimaliseren; het geluid blijft spelen terwijl de gebruiker een spel zoals Sokoban speelt.
- UI Engine: Een interface met formulieren, knoppen, selectievakjes, lijsten, invoervelden en dialoogvensters. De engine regelt toetsaanslagen, tabbing, het opnieuw tekenen van het scherm en het verzenden van events naar de actieve taak.
- Applicaties: Een set aan taken waaronder een bestandsbeheerder, videospeler, muziekspeler, klok, rekenmachine, Sokoban, Snake en Game of Life.
- Videospeler: Ondersteunt videobeelden van 24 fps (en hoger) met geluid, gebruikmakend van een aangepast bestandsformaat: een XBM-stream en MP3 in één enkel bestand.
Technische Beperkingen en Architectuur
Het gehele systeem draait op slechts 8 KB RAM, waarvan de helft al wordt ingenomen door globale variabelen (zoals de video- en SD-buffers, FreeRTOS en servicevariabelen). Deze extreme schaarste aan geheugen bepaalt de volledige architectuur, van de datastructuur tot de opbouw van de taken.
De architectuur is desondanks open voor uitbreiding. Een nieuwe taak is een klasse met setup en loop (vergelijkbaar met een standaard Arduino-sketch), aangevuld met methoden voor het menu en een icoon. De shell regelt de registratie, stack, switching en events. Taken zijn onafhankelijk van elkaar; ze communiceren via de UI engine.
---
Projectlogboeken
Overstap naar FreeRTOS en de UI Engine
Tijdens de ontwikkeling van de bestandsbeheerder (met functies als kopiëren, verwijderen en mappen maken) ontstond de noodzaak voor een gestandaardiseerde gebruikersinterface. Voorheen moest elke taak eigen knoppen en velden tekenen en toetsaanslagen parseren, wat leidde tot veel dubbele code.
De oplossing was een UI-engine waarbij de taak beschrijft wat getoond moet worden, terwijl het tekenen, de focus en event-handling door één centraal mechanisme worden afgehandeld.
Daarnaast was er een structureel probleem met de timing van het scherm. Het scherm vereist regelmatige updates via de lcd.service() methode. In een single-tasking model blokkeerden taken (bijvoorbeeld bij het wachten op een "ja/nee" bevestiging) de shell, waardoor het scherm niet meer werd bijgewerkt. Coroutines werden overwogen, maar verworpen omdat de code dan niet meer sequentieel leesbaar was.
Uiteindelijk is gekozen voor FreeRTOS. Dit vereiste een volledige herschrijving van het project. De nieuwe structuur bevat:
- Basis-widgets: knoppen, selectievakjes en invoervelden.
- Containers: formulieren die widgets beheren, met een stack van formulieren zodat nieuwe schermen over huidige schermen kunnen worden geopend.
- Standaarddialoogvensters: berichtenboxen, waarde-invoer en bestandsdialogen (openen/opslaan).
Vanwege de overstap naar multitasking moest het geheugengebruik drastisch worden geoptimaliseerd door task-stacks te verkleinen en datastructuren te herzien.
Videoweergave en Audio
De mogelijkheid om video af te spelen ontstond nadat bleek dat het scherm snel genoeg kon worden ververst. Na optimalisaties van de dataoverdracht werd de ververstijd per frame teruggebracht van 40 ms naar ongeveer 32 ms.
Het videoproces:
- Een Python-converter extraheert frames uit een videobestand.
- De frames worden via brightness thresholding omgezet naar monochroom 128 × 64.
- Op het apparaat worden de frames in blokken van 1024 bytes vanaf de microSD gelezen naar de videobuffer en naar het scherm gestuurd.
- Om de beeldkwaliteit te verbeteren, is temporele dithering toegepast: pixels knipperen tussen frames om grijstinten te simuleren.
Audio-integratie en CPU-optimalisatie: Voor audio is de VS1053 decoder gebruikt. Een uitdaging was dat de VS1053 constant gevoed moet worden met data, terwijl de CPU tijdens het versturen van een videoframe naar het display (32 ms per frame) grotendeels wacht op de hardware. Uit metingen bleek dat de CPU 99% van de tijd niets deed tijdens het wachten op het display.
De oplossing hiervoor is een tweeledig systeem:
- ISR op Timer 5: Een Interrupt Service Routine die elke 8 $\mu$s wordt aangeroepen om te controleren of het display klaar is voor het volgende byte.
- Functie
service(): Deze wordt regelmatig aangeroepen. Als er een "dirty line" (een gewijzigde regel) wordt gevonden, stuurt deze de adrescommando's naar het display en start Timer 5.
Constructie van het Toetsenbord
Het toetsenbord is een volledige QWERTY-indeling met 64 toetsen, georganiseerd in een 8 × 8 matrix (fysiek een 11 × 6 rechthoek). De PCB is ontworpen in EasyEDA en gefreesd op een CNC3018.
Technische details van het toetsenbord:
- Scanning: De microcontroller activeert per kolom de pinnen en leest de status van de rijen. Softwarematige debouncing voorkomt valse triggers.
- Revisie: In een latere versie zijn diodes per toets toegevoegd voor betrouwere detectie van toetscombinaties en is een 4-naar-16 decoder gebruikt voor de kolomselectie. Hierdoor zijn slechts vier pinnen nodig voor de kolommen, wat ruimte laat voor toekomstige uitbreidingen.
- Labeling: Na experimenten met 3D-printen, lasers en UV-maskers, is er gekozen voor de toner transfer methode voor scherpe en duurzame tekens op de toetsen.
Ontstaan van het Project
Het project is voortgekomen uit een interesse in computergeschiedenis en retro-computers. De vraag was: wat kan een AVR-microcontroller van 16 MHz vandaag de dag doen in vergelijking met computers uit het i386-tijdperk?
Ontwerpprocessen:
- Controller: Er is gekozen voor de Mega 2560 Pro Mini vanwege het compacte formaat en de reproduceerbaarheid voor anderen.
- Display Driver: Er is een eigen driver geschreven voor het ST7920-scherm in parallelle modus voor maximale snelheid. Er is een video-buffer van 1 KB in het SRAM toegewezen.
- Tekstrendering: De grootste uitdaging was de implementatie van fonts van verschillende groottes (tot 16 × 16). Omdat tekst vanaf een willekeurige horizontale positie kan beginnen, moet de driver werken met complexe shifts en masks om data correct in de bytes van de videobuffer te schrijven.
- Shell: Na de graphics-functies is een shell gebouwd met een menu en een basis
Task klasse.
AVR-Laptop: Een 8-bit MCU ver buiten de oorspronkelijke ontwerpdoelen drijven
Systeemoverzicht
Hardware
Het apparaat is gehuisvest in een 3D-geprinte behuizing met afmetingen van 12 × 11 × 3,4 cm. De interne componenten bestaan uit:
- Een zelfgemaakt QWERTY-toetsenbord.
- Een monochroom 128 × 64 LCD-scherm.
- Een VS1053 audio-decoder met luidsprekers.
- Een microSD-kaartlezer.
- Een Real-Time Clock (RTC).
- Een 18650-batterij.
Software en Functionaliteiten
De firmware is gebaseerd op FreeRTOS. De laptop beschikt over de volgende mogelijkheden:
- Multitasking: Echte multitasking met Alt-Tab schakelen. Het is mogelijk om bijvoorbeeld een video te starten en deze te minimaliseren; het geluid blijft spelen terwijl de gebruiker een spel zoals Sokoban speelt.
- UI Engine: Een interface met formulieren, knoppen, selectievakjes, lijsten, invoervelden en dialoogvensters. De engine regelt toetsaanslagen, tabbing, het opnieuw tekenen van het scherm en het verzenden van events naar de actieve taak.
- Applicaties: Een set aan taken waaronder een bestandsbeheerder, videospeler, muziekspeler, klok, rekenmachine, Sokoban, Snake en Game of Life.
- Videospeler: Ondersteunt videobeelden van 24 fps (en hoger) met geluid, gebruikmakend van een aangepast bestandsformaat: een XBM-stream en MP3 in één enkel bestand.
Technische Beperkingen en Architectuur
Het gehele systeem draait op slechts 8 KB RAM, waarvan de helft al wordt ingenomen door globale variabelen (zoals de video- en SD-buffers, FreeRTOS en servicevariabelen). Deze extreme schaarste aan geheugen bepaalt de volledige architectuur, van de datastructuur tot de opbouw van de taken.
De architectuur is desondanks open voor uitbreiding. Een nieuwe taak is een klasse met setup en loop (vergelijkbaar met een standaard Arduino-sketch), aangevuld met methoden voor het menu en een icoon. De shell regelt de registratie, stack, switching en events. Taken zijn onafhankelijk van elkaar; ze communiceren via de UI engine.
---
Projectlogboeken
Overstap naar FreeRTOS en de UI Engine
Tijdens de ontwikkeling van de bestandsbeheerder (met functies als kopiëren, verwijderen en mappen maken) ontstond de noodzaak voor een gestandaardiseerde gebruikersinterface. Voorheen moest elke taak eigen knoppen en velden tekenen en toetsaanslagen parseren, wat leidde tot veel dubbele code.
De oplossing was een UI-engine waarbij de taak beschrijft wat getoond moet worden, terwijl het tekenen, de focus en event-handling door één centraal mechanisme worden afgehandeld.
Daarnaast was er een structureel probleem met de timing van het scherm. Het scherm vereist regelmatige updates via de lcd.service() methode. In een single-tasking model blokkeerden taken (bijvoorbeeld bij het wachten op een "ja/nee" bevestiging) de shell, waardoor het scherm niet meer werd bijgewerkt. Coroutines werden overwogen, maar verworpen omdat de code dan niet meer sequentieel leesbaar was.
Uiteindelijk is gekozen voor FreeRTOS. Dit vereiste een volledige herschrijving van het project. De nieuwe structuur bevat:
- Basis-widgets: knoppen, selectievakjes en invoervelden.
- Containers: formulieren die widgets beheren, met een stack van formulieren zodat nieuwe schermen over huidige schermen kunnen worden geopend.
- Standaarddialoogvensters: berichtenboxen, waarde-invoer en bestandsdialogen (openen/opslaan).
Vanwege de overstap naar multitasking moest het geheugengebruik drastisch worden geoptimaliseerd door task-stacks te verkleinen en datastructuren te herzien.
Videoweergave en Audio
De mogelijkheid om video af te spelen ontstond nadat bleek dat het scherm snel genoeg kon worden ververst. Na optimalisaties van de dataoverdracht werd de ververstijd per frame teruggebracht van 40 ms naar ongeveer 32 ms.
Het videoproces:
- Een Python-converter extraheert frames uit een videobestand.
- De frames worden via brightness thresholding omgezet naar monochroom 128 × 64.
- Op het apparaat worden de frames in blokken van 1024 bytes vanaf de microSD gelezen naar de videobuffer en naar het scherm gestuurd.
- Om de beeldkwaliteit te verbeteren, is temporele dithering toegepast: pixels knipperen tussen frames om grijstinten te simuleren.
Audio-integratie en CPU-optimalisatie: Voor audio is de VS1053 decoder gebruikt. Een uitdaging was dat de VS1053 constant gevoed moet worden met data, terwijl de CPU tijdens het versturen van een videoframe naar het display (32 ms per frame) grotendeels wacht op de hardware. Uit metingen bleek dat de CPU 99% van de tijd niets deed tijdens het wachten op het display.
De oplossing hiervoor is een tweeledig systeem:
- ISR op Timer 5: Een Interrupt Service Routine die elke 8 $\mu$s wordt aangeroepen om te controleren of het display klaar is voor het volgende byte.
- Functie
service(): Deze wordt regelmatig aangeroepen. Als er een "dirty line" (een gewijzigde regel) wordt gevonden, stuurt deze de adrescommando's naar het display en start Timer 5.
Constructie van het Toetsenbord
Het toetsenbord is een volledige QWERTY-indeling met 64 toetsen, georganiseerd in een 8 × 8 matrix (fysiek een 11 × 6 rechthoek). De PCB is ontworpen in EasyEDA en gefreesd op een CNC3018.
Technische details van het toetsenbord:
- Scanning: De microcontroller activeert per kolom de pinnen en leest de status van de rijen. Softwarematige debouncing voorkomt valse triggers.
- Revisie: In een latere versie zijn diodes per toets toegevoegd voor betrouwere detectie van toetscombinaties en is een 4-naar-16 decoder gebruikt voor de kolomselectie. Hierdoor zijn slechts vier pinnen nodig voor de kolommen, wat ruimte laat voor toekomstige uitbreidingen.
- Labeling: Na experimenten met 3D-printen, lasers en UV-maskers, is er gekozen voor de toner transfer methode voor scherpe en duurzame tekens op de toetsen.
Ontstaan van het Project
Het project is voortgekomen uit een interesse in computergeschiedenis en retro-computers. De vraag was: wat kan een AVR-microcontroller van 16 MHz vandaag de dag doen in vergelijking met computers uit het i386-tijdperk?
Ontwerpprocessen:
- Controller: Er is gekozen voor de Mega 2560 Pro Mini vanwege het compacte formaat en de reproduceerbaarheid voor anderen.
- Display Driver: Er is een eigen driver geschreven voor het ST7920-scherm in parallelle modus voor maximale snelheid. Er is een video-buffer van 1 KB in het SRAM toegewezen.
- Tekstrendering: De grootste uitdaging was de implementatie van fonts van verschillende groottes (tot 16 × 16). Omdat tekst vanaf een willekeurige horizontale positie kan beginnen, moet de driver werken met complexe shifts en masks om data correct in de bytes van de videobuffer te schrijven.
- Shell: Na de graphics-functies is een shell gebouwd met een menu en een basis
Task klasse.