[Elek.]: Ontwikkeling van een GPS fietscomputer – Intro en de boot loader (fase 1)

Door Anteros op zaterdag 27 juni 2015 18:20 - Reacties (20)
Categorie: Technisch, Views: 4.283

Introductie

Mijn eerste blog op Tweakers. Een probeersel om te zien of het wat voor mij is. Op zich ben ik niet zo'n schrijver maar het leek me leuk om het eens te proberen.

Anyway, waarom een blog over het ontwikkelen van een GPS fietscomputer? Wel, in mijn vrije tijd pak ik regelmatig mijn racefiets om er op uit te trekken. Het is voor mij een stuk ontspanning en ik kan erg genieten van de snelheid en de vrijheid die het me geeft. Omdat ik regelmatig op nieuwe plekken kom die niet (erg) bekend zijn voor mij, gebruik ik een Garmin Edge 810 GPS fietscomputer met navigatie. Ideaal om thuis een route uit te stippelen en deze vervolgens te volgen met behulp van de fietscomputer.

Maar helaas, het ding doet niet alles wat Garmin beweerd. Althans, niet stabiel. Als datalogger of als map-viewer werkt deze prima, maar de echte navigatiemogelijkheden zoals actieve begeleiding laten het vaak afweten. Sterker nog, soms laat heel de Edge 810 het afweten. Wat er ook nog eens bij moet komen is dat Garmin erg, erg slecht omgaat met klachten en softwareproblemen. De laatste firmware versie voelt nog steeds aan als BETA terwijl de 810 al ruim 1,5 jaar op de markt is.

http://static.tweakers.net/ext/f/U1VNtvSmNxm40umnrdKo4URv/full.png

Dat heeft mijn o.a. doen besluiten om zelf iets te gaan ontwikkelen. De discutabele software is echter niet de enige reden. Een andere reden is dat er diverse (motiverende) features niet, of niet goed geÔmplementeerd zijn. In mijn ogen kan dat (veel) beter en ik heb enkele features in mijn hoofd zitten die het fietsen – en sporten in het algemeen – extra kunnen stimuleren en nog leuker kunnen maken. Een laatste reden is dat ik graag embedded software ontwerp/ontwikkel en graag met elektronica bezig ben. De stap om het zelf te proberen was daarom snel gemaakt.

Ik werk ruim tien jaar als professioneel embedded software engineer en kreeg meteen kriebels om enkele interessante concepten verder uit te werken. Niet alleen op functioneel niveau, maar ook op architectuur niveau. Ook de bijbehorende hardwareontwikkeling lijkt me een leuke uitdaging.

Nu weet ik (van mezelf) dat vele (hobby-)projecten vroegtijdig stranden. En om eerlijk te zijn, vind ik dat helemaal niet erg. Je leert er altijd van en het houdt je scherp. Het is in mijn ogen dus nooit een nutteloze exercitie. Mocht het echter wel zover komen dat er een werkend prototype is, dan ben ik voornemens om serieuze stappen te maken voor een vervolgtraject. Dit zou dan een soort van kickstarter-traject kunnen zijn met als eerste doel een echt produceerbaar product te maken. Het tweede (voor mij utopisch) doel zou zijn om te groeien tot een bedrijfje wat meer onderscheidende producten voor de sportwereld gaat maken.

Overigens weet ik heel goed wat er allemaal bij komt kijken als je een product wilt maken vanuit een prototype: verificatietesten, schok-/vibratie-/emc-/IP67-/etc-testen, normeringen, maakbaarheid, re-designs, issues, lead-time, fine-tunen, handleidingen, reduceren van component-costs, (design-)documentatie, backend, support, etc, etc. Teveel om op te noemen. Het is in ieder geval een grote, moeizame en kostbare stap om van prototype naar product te gaan. Maar goed, zover is het nog (lang) niet :).

Wat kan je verwachten van deze blogs?

In het kort, niks :). Ik ga het traject in zonder bestaande code of architectuur en ik wil dus alles zelf ontwerpen, programmeren en testen. Uiteraard ga ik ook gebruik maken van bestaande, open-source, free-to-use software. Dit gehele ontwikkelproces wil ik af en toe delen middels enkele blog-posts om anderen wellicht te inspireren. Het kan ook zijn dat ik juist een blog-post schrijf om mijn frustratie weg te typen of om advies te vragen (je bent nooit te oud om te leren :)). Omdat ik de meeste tijd wil besteden aan het project, heb ik niet de tijd om de gehele context met alle ins en outs te beschrijven. Het kan dus goed mogelijk zijn dat er bepaalde informatie afwezig is, welke cruciaal is voor de context of het begrip voor de gemaakte keuzes. In dat geval, op voorhand mijn excuses. Uiteraard kan ik later het een en ander nader toelichten mocht dat nodig zijn.

Dus: verwacht niks, maar mocht er een blog-post verschijnen dan hoop ik dat het interessant genoeg is om te lezen of vragen te stellen.

Tot zover de introductie, op naar het interessante gedeelte :P

Projectinformatie en huidige status

Een lijstje met hardware eigenschappen welke ik voor ogen heb voor dit project:
  • GPS voor snelheid, locatie, logging en navigatie
  • BT voor communicatie met sensoren (BLE) en een telefoon
  • ANT+ ondersteuning voor sensoren
  • Transflective color LCD met een nog onbekende resolutie
  • Micro SD-card interface voor kaarten, opgeslagen ritten en statistieken, etc
  • WiFi
  • Diverse interne sensoren zoals luchtdruk, temperatuur, etc
  • Er is nog meer maar dit schiet me zo ff te binnen
Omdat hardwareontwikkeling tijdrovend en duur is, ga ik eerst gebruikmaken van een ontwikkelbord in combinatie met externe modules zoals WIFI, BT (LE), ANT+, GPS en een LCD. Zo'n ontwikkelbord bevat verder vaak een RS232 poort, USB, EEPROM, I2C, GPIO, SDCARD, etc. en is dus ideaal om te gebruiken voor het ontwikkelen van een prototype.

Gezien de toepassing, een GPS fietscomputer met navigatie mogelijkheid, is een energiezuinige maar relatief krachtige microcontroller vereist. Omdat zo'n fietscomputer normaliter klein moet zijn, is het niet handig om een grote print met diverse externe componenten te hebben. Integratie is key en daarom kies ik voor een microcontroller met geÔntegreerde FLASH, RAM en diverse periferie. Daarvan zijn er tig op de markt, maar aangezien ik de meeste ervaring heb met ARM ťn omdat ik toevallig al een ARM ontwikkelbord heb liggen, valt voorlopig mijn keuze op een Cortex M3 van ST. Een STM32F207 om precies te zijn. Deze microcontroller heeft de mogelijkheid om extern geheugen aan te spreken mocht de hoeveelheid interne RAM niet voldoende zijn.

http://static.tweakers.net/ext/f/NoAhPcgCqMQADktACRCq7KZk/full.png

De software, volledig geschreven in C, wordt gelaagd ontwikkeld met diverse abstracties. Dit werkt hergebruik van software componenten in de hand en maakt het relatief eenvoudig om later voor een meer performante microcontroller te kiezen, mocht dat nodig zijn.

De eerste stap was het inrichten van de ontwikkelomgeving waar ik nu bijna klaar mee ben. De gebruikte tooling is allemaal freeware: Gevirtualiseerde Linux als ontwikkel-host, Eclipse voor de IDE, ARM cross compiler voor compilatie en linken, MAKE/MAKEFILE voor de build environment en GIT als VCS. Voor code documentatie maak ik gebruik van Doxygen terwijl ik LibreOffice gebruik voor mijn design documentatie.

Omdat ik bottom-up ga ontwikkelen is de volgende stap het ontwerpen en programmeren van de boot loader. Dit is een cruciaal stukje software welke diverse verantwoordelijkheden en mogelijkheden krijgt. De boot loader maakt het tevens voor mij mogelijk om snel nieuwe firmware te flashen zonder gebruik te maken van speciale apparatuur.

Boot loader – High Level

De boot loader is het eerste stukje software wat uitgevoerd gaat worden als de microcontroller spanning krijgt. Deze heeft daarom een primaire taak om de microcontroller en eventuele andere componenten zoals het externe geheugen te initialiseren. Na deze initialisatie geeft de boot loader de controle over aan de applicatie.

In veel deep-embedded applicaties wordt de boven genoemde initialisatie uitgevoerd door de applicatie zelf en is er geen boot loader. Dat is een ontwerpkeuze maar in mijn ogen niet altijd de juiste. Waarom? Wel, een boot loader biedt namelijk vele mogelijkheden:
  • Het voorkomen van het bricken van een product als er een firmware update uitgevoerd wordt
  • Hardware en platform abstractie van een of meerdere componenten en periferie
  • Eenvoudige manier om een nieuwe applicatie firmware te flashen
  • Een applicatie interface beschikbaar stellen voor primaire functies (soort van BIOS)
  • Data 'hiding' door bepaalde data alleen via een interface beschikbaar te stellen aan de applicatie (secure firmware update, device profile data, key management, hardware revisie, etc)
  • Etc.
Het is eerlijk om ook enkele nadelen te noemen:
  • Start proces is wat complexer
  • Boot loader neemt wellicht kostbare FLASH ruimte in
  • Opstarttijd kan wat langer zijn (maar scheelt vaak maar enkele ms)
  • Build systeem is wat complexer (aparte linker files, makefiles, etc)
  • Van tevoren goed nadenken over de geheugenindeling
Doordat het hebben van een boot loader echter zoveel meer mogelijkheden biedt, wegen de nadelen niet op tegen de voordelen. Een boot loader gaat er dus komen :-)

Omdat de boot loader en applicatie apart gelinkt worden zijn het twee aparte entiteiten – ze delen geen object code –. Om ervoor te zorgen dat ze elkaar niet in de weg gaan zitten wanneer ze gedeelde resources zoals het geheugen aanspreken, moet van tevoren goed nagedacht worden hoe de boot loader en applicatie gebruik gaan maken van deze resources. Ook moet duidelijk zijn hoe beide images opgeslagen worden in het FLASH geheugen.

Voor mijn ontwikkelplatform gaat die geheugenindeling er voorlopig als volgt uitzien:

http://static.tweakers.net/ext/f/tQgbXSf2HQVPWlRbBw9GGaI9/full.png

Ik begrijp dat het bovenstaande plaatje wellicht enige uitleg behoeft.

De boot loader en applicatie hebben ieder een aparte sectie gereserveerd gekregen in het interne FLASH geheugen van de microcontroller. Deze secties liggen vast en zijn gedefineerd in de twee zelfgeschreven linker files. Omdat de boot loader in principe nooit geŁpdatet wordt in het veld, moet goed nagedacht worden over deze secties. Een te grote boot loader sectie zorgt ervoor dat er minder over blijft voor de applicatie sectie. Omgekeerd hetzelfde. Een goede balans vinden is dus cruciaal om eventuele problemen in de toekomst te voorkomen.

De boot loader heeft een klein gedeelte genaamd “BL API”. Dit is tabel met functie-pointers welke verwijzen naar functies in de boot loader code. Dit gedeelte staat altijd aan het eind van de boot loader sectie, ongeacht de lengte van de boot loader code. De ruimte tussen de “BL API” en de boot loader code wordt opgevuld met filler bytes zoals 0xFF. Door het op deze manier te implementeren 'weet' de applicatie altijd de “BL API” te vinden ongeacht de werkelijke grote van de boot loader code. De applicatie hoeft dan ook niet geŁpdatet te worden als de boot loader geŁpdatet wordt.

http://static.tweakers.net/ext/f/PwbRtSRKBjMhgaDVdyepFdsO/full.png

De applicatie kan gebruik maken van de “BL API” om bepaalde services van de boot loader aan te spreken. Denk dan b.v. aan asymmetrische decryptie (handtekening verificatie), toegang tot (gedeelde) externe permanente opslag zoals een EEPROM – hardware abstractie voor de applicatie –, uitlezen van de hardwarerevisie, producttype, het uitvoeren van een firmware update (FWU), toekennen van boot loader extensies, etc. Deze tabel is opgeslagen in de boot loader sectie maar zowel de boot loader linker file als de applicatie linker file bevatten verwijzingen er naartoe.

http://static.tweakers.net/ext/f/O6LiStbUUohIzj0tUA7OYog5/full.png

Wat nog opvalt aan de boot loader en applicatie images zijn de IVT's (Interrupt Vector Tabellen). Elk image heeft zo'n tabel. In deze tabel staan functie-pointers naar ISR (Interrupt Service Routine) functies voor de relevante (externe) interrupts. Ook de reset-vector staat erin. Omdat zowel de boot loader als de applicatie hun eigen interrupt handlers hebben, krijgen ze dus ook ieder een eigen IVT. Voordat de boot loader de applicatie image aanroept, wordt de microcontroller eerst geherconfigureerd zodat deze de applicatie IVT gebruikt als er zich een (externe) interrupt voordoet.

Als laatste de check-sums. Zowel de applicatie image als de boot loader image hebben een check-sum veld. Deze check-sums worden berekend na het genereren van de binaries door het build systeem om vervolgens eraan toegevoegd te worden. Het doel ervan is om tijdens het booten te kunnen bepalen of er code-corruptie is ontstaan in ťťn of beide images. Het kan namelijk voorkomen dat er door b.v. straling ťťn of meerdere bits omvallen in het flash geheugen. Dit kan zich dan uiten in willekeurig gedrag van de microcontroller of zelfs een complete crash. De boot loader berekent run-time de check-sums van zichzelf en de applicatie image om deze vervolgens te vergelijken met de opgeslagen check-sums. Zijn ze hetzelfde dan gaat boot proces verder. Zijn ze niet hetzelfde, dan stopt het boot proces en wordt er een error gegenereerd. Mocht de applicatie image corrupt zijn dan kan de gebruiker deze opnieuw flashen. Is de boot loader echter corrupt, dan houdt het op.

http://static.tweakers.net/ext/f/slENhojRSh0Qahh5eFf8auAi/full.png

Nu het RAM gedeelte. Bovenaan is een stukje “exclusief BL” te zien. Dit is een gedeelte van het RAM geheugen wat exclusief gereserveerd is voor de boot loader. Het is de bedoeling dat de applicatie hier niet van leest of in schrijft – echter kan de gekozen microcontroller dit niet hardwarematig voorkomen (geen MMU) –. Dit stukje RAM geheugen wordt middels een linker file geconfigureerd. De boot loader gebruikt dit om tijdelijke runtime data in op te slaan als de applicatie gebruik maakt van de BL API – de zogenaamde 'statische'-variabelen –.

Als laatste het mirrored RAM gedeelte. Normaliter is deze 'mirror' mode niet nodig, ook niet voor een standaard applicatie firmware update (FWU). Moet echter de boot loader geŁpdatet worden, dan moet deze speciale mogelijkheid wel aangesproken worden. Het is een optie van de STM32F2xx en deze maakt het mogelijk om het interne RAM geheugen te mappen op dezelfde adressen als het interne FLASH geheugen – dit ligt iets genuanceerder maar voor deze blog is het voldoende –. Dit maakt het mogelijk voor de boot loader om eerst zichzelf te kopiŽren naar het interne RAM geheugen om vervolgens de mirror-mode aan te zetten. Executie van de boot loader gaan dan onverstoord vanuit het RAM geheugen verder. Hiermee komt het interne FLASH beschikbaar om gewist en opnieuw geprogrammeerd te worden. Op deze manier kan je dus zowel de boot loader als de applicatie opnieuw flashen. Merk op dat wanneer de boot loader opnieuw geflashed wordt, er wel een kans op bricken bestaat.

Wat verder….

Zoals ik al eerder aangegeven heb, is mijn ontwikkelomgeving en build systeem op orde en kan de volgende stap genomen worden. Deze stap behelst het creŽren van de linker-files en het opzetten van de boot loader architectuur. Mijn volgende blog gaat dan ook wat dieper in op de linker-files en hoe deze gebruikt worden in zowel code als de linker. Tevens zal ik wat dieper ingaan op de boot loader architectuur en de eerste resultaten van de implementatie.

Slot

Uiteindelijk is het toch meer type-werk geworden dan gedacht maar desalniettemin hoop ik dat het (een beetje) interessant was. Mijn streven is om de volgende keer meer plaatjes en minder tekst te plaatsen :)

Anyway, bedankt voor het lezen.

References en links:
Eclipse C/C++
ARM cross compiler
GIT
STM32F2xx BSP+FreeRTOS+CMSIS (STM cube)
STM32F2xx reference manual – Cortex M3 reference manual