zaterdag 7 maart 2020

Software - De list (3: TM1637)

Het dashboard van de Burton wordt volledig custom en digitaal. Geen analoge meters dus. Met de precieze lay-out en het design ben ik nog niet klaar, maar er komen in ieder geval grote 7-segmentdisplays in.


https://en.wikipedia.org/wiki/Seven-segment_display


En daar gaan we weer. Een 7-segmentdisplay zou zomaar - de naam verraad het al - zeven uitgangen van de microcontroller kunnen bezetten. Elk segment moet namelijk onafhankelijk aangestuurd kunnen worden. Om de snelheid van de Burton weer te kunnen geven zijn minimaal drie cijfers en dus drie van die 7-segmentdisplays nodig. Da's alleen al 21 uitgangen. Zoveel uitgangen heeft de ESP32 helemaal niet vrij, dus dat moet anders kunnen. En gelukkig kan dat ook anders. Met een I²C-bus I/O-expander bijvoorbeeld. Zou kunnen, maar in de praktijk worden vaak andere IC's gebruikt. Bijvoorbeeld de TM1637 van Titan Micro Electronics. Een speciaal IC waarmee tot zes 7-segmentdisplays aangestuurd kunnen worden.

TM1637, display driver.

Dit IC is nog tot meer in staat, maar daar maak ik geen gebruik van. Net als de I²C-bus wordt dit IC aangestuurd met een klok- en een datasignaal. Ook het communicatieprotocol lijkt erg veel op het I²C-protocol, met het verschil dat de TM1637 niet geadresseerd hoeft te worden. Je kunt maar één zo'n IC aanspreken.

Bij de NeoPixels uit het vorige bericht was de grootste uitdaging de timing van de bits die verstuurd moeten worden. Bij deze TM1637 is de timing veel minder kritisch. Zolang het klok- en datasignaal maar netjes op elkaar afgestemd zijn. In de figuur hieronder wordt dit weergegeven:


Timing van de TM1637

Elke bericht naar de TM1637 begint altijd met een start-conditie en eindigt altijd met een stop-conditie. Bij de start wordt het datalijntje (DIO) laag gemaakt terwijl de kloklijn (CLK) hoog is. Bij de stop wordt de DIO juist hoog gemaakt terwijl de CLK hoog is. Dit zijn de enige twee condities waarbij de DIO van niveau wijzigt terwijl CLK hoog is. De TM1637 kan hierdoor deze twee belangrijke condities herkennen.

Een commando of data (tussen start- en stop-conditie) wordt verstuurd in een groepje van acht bits. Ná elk zo'n byte volgt nog een Acknowledge (ACK) waarbij de TM1637 de kans krijgt om het ontvangen byte te bevestigen. De acht bits worden door de TM1637 één voor één 'ingeklokt' op het moment dat het kloksignaal van laag naar hoog beweegt. Easy peasy.

In de software worden bovenstaande condities op de volgende manier geprogrammeerd (voorbeeld van de start-conditie):

TM1637 Start-conditie

Omdat de absolute timing niet zo kritisch is kan ik gewoon gebruik maken van de eenvoudige pauzefunctie vTaskDelay(1). Hiermee wordt steeds 1 tick, ofwel 10 ms gewacht tussen de bitwisselingen. Een eeuwigheid als je het vergelijkt met de timing van de eerder genoemde NeoPixels. Een prima oplossing voor dit doeleind. Bovendien krijgen andere taken gedurende deze pauzes de tijd om hun ding te doen. Uiteindelijk gebruikt dit protocol bijzonder weinig processortijd.

Voordat de 7-segmentdisplays aangestuurd kunnen worden moet de TM1637 eerst geconfigureerd worden. Hierbij wordt een keuze gemaakt of de TM1637 gebruikt wordt voor het aansturen van een display of het uitlezen van een keyboard (het K-bit) en of de (maximaal) zes 7-segmentdisplays afzonderlijk of automatisch ná elkaar geadresseerd worden (het F-bit).

Bit:  7 6 5 4 3 2 1 0
Data: 0 1 0 0 0 F K 0

In mijn geval wordt zowel het K-bit (bit 1) als het F-bit (bit 2) laag gehouden. Dus:

Bit:  7 6 5 4 3 2 1 0
Data: 0 1 0 0 0 0 0 0

In de software ziet dit er als volgt uit:

TM1637 Configuratiebericht


Daarna wordt het display aangezet met een bepaalde helderheid:

Bit:  7 6 5 4 3 2 1 0
Data: 1 0 0 0 S A B C

Het S-bit zet het display aan. De drie bits A, B en C bepalen de helderheid van het display. De helderheid (brightness) loopt van 000 naar 111 en heeft dus acht verschillende standen. In de software gebruik ik hiervoor een eenvoudige functie:


TM1637 Brightness

Nu de TM1637 geconfigureerd is, kunnen de 7-segmentdisplays aangestuurd worden. De TM1637 wil eerst weten welke van de (maximaal) zes displays gewijzigd moet worden. We starten altijd bij display 0. Het adres wordt na het eerste cijfer automatisch opgehoogd, dat is met het F-bit (zie hierboven) zo geconfigureerd. In geval van zes cijfers worden achtereenvolgend de volgende commando's naar de TM1637 gestuurd:

  1. Start
  2. Adres ( = 0 )
  3. Ackn
  4. Cijfer 1
  5. Ackn
  6. Cijfer 2
  7. Ackn
  8. Cijfer 3
  9. Ackn
  10. Cijfer 4
  11. Ackn
  12. Cijfer 5
  13. Ackn
  14. Cijfer 6
  15. Ackn
  16. Stop

Dat is alles. Hoewel... De cijfers bestaan uit 7 segmenten. Bij elk cijfer moet een andere combinatie van segmenten oplichten. Het cijfer moet hiervoor omgezet worden naar een bitpatroon. Hiervoor is de software voorzien van een constant array met tien posities:


Van decimaal naar 7-segment

De bitpatronen weerspiegelen de op te lichten segmenten. De positie in het array bepaalt het cijfer dat gepresenteerd moet worden. Bij het achtste element (overeenkomstig het cijfer 8) is goed te zien dat alle zeven segmenten op moeten lichten.

Alle puzzelstukje bij elkaar ziet de functie om een getal (number) met al zijn cijfers (digits) te presenteren er als volgt uit:


Getal presenteren op het display


Met deze derde 'list' is het gelukt om maximaal zes cijfers op een display te tonen en hier maar twee digitale uitgangen van de microcontroller voor te gebruiken. Samen met list 1 en list 2 ben ik nu in staat om behoorlijk wat knoppen en lampjes aan te sluiten. Maar ik wil nog meer... Binnenkort list nummer vier!

vrijdag 6 maart 2020

Software - De list (2: NeoPixels)

Een tweede list voor het minieme aantal ingangen en uitgangen van de ESP32 wordt bedacht voor de aansturing van alle indicatie- en storingslampjes. De dashboardlampjes zouden aangestuurd kunnen worden met de I/O-expanders uit het vorige bericht. Maar dat zou te gemakkelijk zijn ;-). Het idee is om hiervoor NeoPixels te gaan gebruiken.


https://www.adafruit.com/


NeoPixels zijn RGB-leds die via één digitale uitgang van de ESP32 aan te sturen zijn. Over die digitale uitgang wordt een patroon van bits naar de NeoPixel gestuurd. Precies 24 bits; voor elke kleur - rood, groen en blauw - acht bits. Met die acht bits wordt de intensiteit van elke kleur ingesteld (0..255) en kunnen dus 256 x 256 x 256 = ruim 16 miljoen kleuren gekozen worden. Mooi, één digitale uitgang voor 16 miljoen kleuren! Maar het wordt nog mooier, want NeoPixels kunnen in een string achter elkaar geschakeld worden; en ook dan is er nog steeds maar één digitale uitgang nodig. Zijn er twee NeoPixels achter elkaar geschakeld, dan worden niet 24, maar 48 bits naar buiten gestuurd. De eerste 24 bits worden door de eerste NeoPixel 'gebruikt', de resterende bits worden doorgestuurd naar de tweede NeoPixel. Zo kunnen er honderden achter elkaar geschakeld worden waarbij elke NeoPixel telkens 24 bits van het treintje bits afsnoept en de rest doorgeeft. Ik wil (o.a.) gebruik maken van een stick met acht NeoPixels:


https://www.adafruit.com/


Voldoende voor alle indicatielampjes en nog voor andere creatieve uitingen. Zo kan zo'n balkje ledjes ook gebruikt worden ter indicatie van het toerental.

Tot nu toe klinkt het allemaal positief. Maar er zit een addertje onder het gras. De NeoPixels zijn behoorlijk kritisch als het gaat om de timing van de pulsjes die ontvangen worden. Dit komt omdat er maar één pin gebruikt wordt voor het bitpatroon en daarnaast niet net als bij I²C-bus een separaat kloksignaal aanwezig is. Een digitale 0 wordt gevormd door de uitgang van de ESP32 eerst gedurende 300 ns (jawel nanoseconde) hoog te maken en vervolgens 800 ns laag. Een digitale 1 wordt herkend als de uitgang 800 ns hoog en 325 ns laag wordt gemaakt.


WS2812-timing

Als de uitgang enkele tientallen microseconden laag blijft (Treset), dan worden de nieuw ontvangen RGB-waarden van alle NeoPixels gebruikt om gelijktijdig alle leds aan te sturen. De leds houden die kleur totdat er een nieuw treintje met kleurenbits is langsgekomen. Omdat de tijdwaarden zo kort zijn, gaat dit allemaal razendsnel. Dit protocol is vastgelegd in de WS2812-specificatie. Er zijn inmiddels enkele verschillende versies van dit protocol waarbij vooral de timing anders is. En omdat juist die timing een beetje kritisch is, is het belangrijk om te weten welke versie van de WS2812-specificatie bij de gebruikte NeoPixels hoort.

De ESP32 werkt - zoals in het vorige bericht vermeld - met een multitasking besturingssysteem genaamd FreeRTOS. De verschillende taken die hierin draaien moeten zo nu en dan wat tijd gunnen aan de andere taken. Normaliter gebeurt dit door een taak even zogenaamd te pauzeren. In een microcontroller zónder multitasking besturingssysteem (bijvoorbeeld een Arduino UNO) zal een delay-functie de processor eventjes bezig houden (blocking) en vervolgens weer met het programma verder gaan. In een multitasking besturingssysteem is dit uit den boze. Het pauzeren van een taak zal de processor niet blokkeren, maar juist de tijd geven wat aandacht aan een andere taak te geven. In een FreeRTOS-taak wordt hiervoor de functie vTaskDelay() gebruikt. Deze functie accepteert een aantal ticks als argument. Eén tick is de kleinste tijdsduur die te gebruiken is om een taak te pauzeren. De lengte van een tick is afhankelijk van de snelheid waarmee FreeRTOS tussen taken schakelt. En dat doet 'hij' standaard 100 keer per seconde. Zo'n tick is hierdoor ongeveer 10 ms. Je ziet direct dat dit véél te lang is om de WS2812-timing voor elkaar te krijgen. Dit moet dus op een andere manier aangepakt worden.

Bij de I²C-bus hadden we dit probleem niet, omdat de timing en afhandeling van die bus in de hardware van de ESP32 wordt uitgevoerd. Voor het WS2812-protocol is echter geen hardware in de ESP32 aanwezig. Of misschien toch wel...

Zoekende op internet en in de documentatie van Espressif (de maker van de ESP32) kwam ik de Remote Control-hardware (RMT) tegen. Net als de CAN-bus en de I²C-bus een stukje hardware in de ESP32 waarmee het bitpatroon van een afstandsbediening gerealiseerd kan worden. De meeste afstandsbedieningen werken met een infrarood-led. Deze led zendt afhankelijk van de ingedrukte knop op de afstandsbediening (en het merk) een ander treintje bitjes naar de ontvanger (bijv. de tv). Ook dit bitpatroon volgt een strikt tijdschema.

Een klein deel van het RMT-blokschema (Espressif)

De RMT-hardware is net als de CAN-bus en de I²C-hardware vooraf te voorzien van een reeks commando's - hier een serie getalletjes waarmee de tijd van het hoog en laag worden van de digitale uitgang bepaald wordt. Vervolgens krijgt de RMT-hardware vanuit de software de opdracht zijn ding te doen. Daar is de software dan niet meer bij betrokken. De precieze timing wordt volledig afgehandeld door de RMT-hardware zelf. Deze heeft een tijdresolutie overeenkomstig de interne klok van de microcontroller. Die draait op 80 MHz, wat overeenkomt met een tijdresolutie van 12.5 ns. Aangezien de timing van de WS2812 in de grootteorde van 100 ns ligt, is dit nauwkeurig genoeg.

Timing-data

De timing-data wordt aangeboden in een array van 32-bits woorden. Die 32-bits wordt gevuld met twee keer een tijdsduur van 15 bits en de resterende twee bitjes om het niveau van het signaal aan te geven. Als ik het niet zou weten, zou ik denken dat het bedacht is voor het WS2812-protocol. Voor elk bit heb je in dit protocol namelijk een Hoog- en een Laag-tijdsduur (zie hierboven) nodig. Om één NeoPixel aan te sturen zijn precies 24 (3 x 8 voor R, G en B) bits nodig; voor de RMT-hardware komt dit neer op een array van 24 x 32-bits woorden. Voor een stick van acht NeoPixels komt dit uit op 8 x 24 x 32 bits. Da's ruim 6000 bits. Voor een doorsnee computer een druppel op een gloeiende plaat, voor een microcontroller best wel wat geheugen. Gelukkig heeft de ESP32 aardig wat geheugen aan boord, dus dat levert hier geen beperkingen op.

Naast de taken die eerder beschreven zijn (CAN-bus en I²C-bus) is er nu weer een belangrijke taak bijgekomen. Het aansturen van NeoPixels via de RMT-hardware. Drie onafhankelijke taken die netjes door FreeRTOS afgehandeld worden. Er komen er nog meer...

zondag 1 maart 2020

Software - De list (1: I²C)

In het vorige bericht werd duidelijk dat het aantal vrije in- en uitgangen (19 stuks) van de microcontroller (ESP32) niet toereikend is voor alle dashboardfuncties van de Burton. Zeker niet omdat ik méér mogelijkheden wil introduceren dan dat er standaard in de eend zitten. Dit vraagt om een list, zou Burton-bouwer Anton Folkeringa zeggen. Zoek op het internet naar "i/o expander" en je komt al snel de PCF8574 van NXP (voorheen Philips) tegen.

https://pmdway.com/products/pcf8574-8-bit-i2c-i-o-expander-dip-5-pack

Dit is een relatief eenvoudig IC waarmee het aantal ingangen en/of uitgangen van de microcontroller uitgebreid kan worden. De PCF8574 wordt met de microcontroller verbonden via een tweedraads I²C-bus (Inter-IC-bus). De ESP32 heeft standaard twee I²C-bussen aan boord, dus daar kunnen we er wel eentje van gebruiken. Van de 19 vrije in- en uitgangen worden er twee gebruikt voor deze tweedraads bus. Eentje voor de klok (SCL) en eentje voor de data (SDA). Eén PCF8574 bevat acht digitale poorten. Ze kunnen als ingang of als uitgang gebruikt worden. De I²C-bus is - zoals de naam al doet vermoeden - een bussysteem waar - net als bij de CAN-bus - meerdere apparaten op aangesloten kunnen worden. Anders dan bij de CAN-bus gaat het bij de I²C-bus echter over het verbinden van IC's en hele korte afstanden, meestal op dezelfde printplaat. Er kunnen dus ook meerdere PCF8574-IC's aan dezelfde bus gekoppeld worden. Drie van de pinnen van het IC worden gebruikt om een digitaal adres in te stellen, van binair 000 tot 111 zijn dit acht verschillende adressen. Uiteindelijk kunnen er van hetzelfde IC dus acht verbonden worden met de microcontroller, resulterend in 8 x 8 = 64 in- of uitgangen. En alsof dat nog niet genoeg is, is er nog de PCF8574A (zie foto hierboven) waarmee nog eens acht IC's aan de bus toe te voegen zijn. Méér dan voldoende dus!

Op internet zijn mooie compacte modules te vinden met handige pinnen voor het aansluiten van de acht in- of uitgangen, de twee lijnen van de I²C-bus, de voeding en het instellen van het 3-bits-adres.

Twee varianten van een PCF8574-module

Voor de ESP32 is een handige bibliotheek beschikbaar waarmee de I²C-hardware van de microcontroller aangestuurd kan worden. De berichten voor op de bus worden eerst voorbereid en netjes op een rij in het geheugen gezet, waarna de I²C-hardware de opdracht krijgt om onafhankelijk van de afloop van het programma de I²C-communicatie af te handelen. De timing op de bus wordt door de hardware van de controller geregeld en is dus niet afhankelijk van de software. Da's mooi, want dan hoeven we ons daar geen zorgen om te maken.


https://www.freertos.org/


De ESP32 bevat twee processor-cores en wordt standaard voorzien van het FreeRTOS realtime operating system. Dat klinkt ingewikkeld, maar is het juist niet. FreeRTOS geeft namelijk de mogelijkheid om met parallel draaiende taken te werken (Multi Tasking). Erg handig als je gelijktijdig meerdere taken af moet handelen. Een van de taken is natuurlijk de communicatie over de CAN-bus. Nu hebben we er een belangrijke taak bij: de afhandeling van de I²C-communicatie. En wacht maar, er komen nog aardig wat taken bij...