In de vorige blogpost liet ik een belletje rinkelen, in deze ga ik een lampje laten branden. Een neonlampje, want ik houd van het oranje licht van neonlampjes en nixiebuizen. In veel nixieklokken zie ik neonlampjes als scheidingsteken tussen de uren en minuten, waarvan maar 1 elektroden licht geeft omdat het lampje op DC wordt gebruikt. Dat kan voor het lampje geen kwaad, maar ik vind het mooier wanneer beide elektroden oplichten. Dus in deze blogpost ga ik beide elektroden van een neonindicatorlampje laten oplichten.

(Chicago Miniature A1C neon indicator lampje)
(Chicago Miniature A1C neon indicator lampje)

De ingewikkelde manier

Met dit schema, gebaseerd op de smps uit vorige blogposts kun je AC maken en op die manier beide electroden van een neonindicatorlampje licht laten geven. (Eigenlijk hetzelfde trucje als het laten rinkelen van de telefoon, maar op een hogere frequentie zodat je het neonlampje niet ziet knipperen en de koppelcondensator kleiner kan.)

Maar dat is een beetje Rube Goldberg. Het werkt wel:

Het kan een stukje simpeler:

De eenvoudiger manier

Door beide elektroden van het neonlampje een weerstand naar + en een transistor naar – te geven, kan vrij eenvoudig in elke richting stroom door het lampje gestuurd worden:

Een volledige H-brug zou energiezuiniger zijn, maar dit is een eenvoudiger en compacter schakeling.

Dit maakt het mogelijk beide elektroden van een neonlampje afzonderlijk te sturen.

Hier een demofimpje van een looplicht / binaire teller. 4 bits, op 2 neonlampjes:

(Als de hieronder ingebedde video niet werkt kun je dezelfde video op youtube kijken)

De sourcecode staat hier, puur voor de compleetheid.

De achtergrond

Beide elektroden van een neon-indicatorlampje afzonderlijk aansturen geeft de mogelijkheid bijvoorbeeld een heel nerdy (binair) neonlampjes-horloge te maken. Er bestaan nixie-horloges, maar een neonlampjeshorloge heb ik nog niet gezien. Of een neonlampjesklok met 30 of 60 lampjes in een cirkel.

In plaats van een neonlampjes-looplicht of -klok zou je ook een wat exotisch type stappenmotor (inchworm piëzo motor) aan kunnen sturen. (Mogelijk wil je dan wel een volledige H-Brug. En een instelbare spanning voor precisie-positionering: iets met een dedicated SMPS-IC en een DAC om het setpoint in te stellen, niet een microcontroller die het naast andere taken doet).

En natuurlijk om beide elektroden licht te laten geven in de scheidingstekens van luxe nixieklokken. Er zijn een aantal makers van hele mooie luxe nixieklokken. Vaak voor bedragen waarvoor ik eerder een oscilloscoop zou kopen (en normale mensen een tweedehands auto). Maar kijken kan.

Dalibor Farny maakt zelfs de nixiebuizen zelf (Hij verteld er van alles over op youtube en op zijn site). Ik weet niet of de seperators in zijn klokken ook zelfgemaakt zijn, maar ik vind ze mooier dan een ‘standaard neonlampje’.

Andere makers, zoals nixieshop gebruiken een IN-3 (of dergelijke), net als mijn nixieklok.

Maar bijvoorbeeld elektor en retrotimes gebruiken ‘gewone’ (NE-2 achtige) neonindicatorlampjes. Omdat je bij deze klokken tegen de bovenkant van de neonindicator op kijkt zie je eigenlijk niet dat er maar 1 electrode licht geeft. Bij Millclock, toch hele mooie luxe klokken, zie je het wel.

(Geen van deze links is gesponsord, en ik heb ook met geen van deze bedrijven ervaring, ik vind het gewoon mooie klokken.)

Door ‘speciale’ neonlampjes (IN-3 en whatever Dalibor Farny gebruikt) als uren/minuten/seconden scheidingstekens te gebruiken, heb je niet dat er maar 1 electrode van een ‘standaard’ neonindicatorlampje oplicht.

Maar, je zou dat effect ook juist kunnen gebruiken: met 2 bits per neonlampje zou je extra informatie kunnen weergeven op de scheidingstekens van een nixieklok. Bijvoorbeeld de (buiten)temperatuur, binair weergegeven in 6 bits op 3 lampjes. Of, met wat meer lampjes, als lineair (bargraph) display van bijvoorbeeld de luchtvochtigheid.

Met de step-up uit de vorige post en wat verdere microcontroller-magie (sinus PWM-en) is het mogelijk een T65 telefoon te laten rinkelen. Dat zou bijvoorbeeld bij toneel of theater gebruikt kunnen worden, ware het niet dat we inmiddels modernere telefoons hebben. Ik gebruik het hier vooral als demonstratie van wat er kan met een step-up en een sinustabel, maar ik snij wat bochtjes kort af. Het is dus geen kant-en-klaar nabouwbaar project.

Als je een GSM modem in een T65 wilt inbouwen om mobiel te bellen met je draaischijftelefoon, of als je een intercomsysteem met meerdere T65’s wilt opzetten, heb je hier niet genoeg aan, maar het kan misschien een deeltje van je project worden.

Goed. Eerst maar eens een filmpje van het resultaat. Tuur niet naar de oscilloscope, plaatjes van het scherm volgen later:

(Als onderstaande ingebedded video niet werkt, kun je dezelfde video op youtube kijken)

Dan de techniek:

Hoe?

Een telefoonlijn biedt normaalgesproken -48 Vdc aan en om de telefoon te laten rinkelen wordt daar in te telefooncentrale 60 tot 105 Vrms bij 20 Hz mee in serie gezet. Of 25 Hz, in Europa. Hoewel ik in Europa woon heb ik in eerste instantie 20 Hz gebruikt voor de demo (ik las pas over het frequentieverschil tijdens het schrijven van de blogpost.) Wordt je telefoonlijn niet gemaakt door een telefooncentrale, maar door bijvoorbeeld een Fritz!box, dan is dat 35 Vdc en als belspanning 32 Vrms.

(Er zal normaalgesproken wel wat spanningsval zijn over de bedrading tussen telefooncentrale en huis, en als de ‘centrale’ in je huis staat in de vorm van een kabelmodem/gateway/fritsbox heb je die spanningsval niet.)

Een schema van het binnenste van een T65:

Schema van T65/W65 telefoontoestel, van Picbasic.nl (https://www.picbasic.nl/frameload.htm?https://www.picbasic.nl/t65-telefoon.htm)
(Deze afbeelding is afkomstig van de Picbasic.nl website. Een Heemaf 1955 is vergelijkbaar.)

Omdat de condensator in de telefoon (C1) gelijkstroom blokkeert, heb ik niet de moeite genomen om -48 VDC in serie te zetten die er daarna toch weer af gaat. Sterker nog: ik maak niet eens AC: de spanning wisselt niet van polariteit. (Zie scope-plaatjes verderop)

Voor een T65 en dergelijke eenvoudige toestellen maakt dat niet uit. De condensator in serie met de bel lost dat wel op. Voor modernere telefoons waar electronica in zit gaat dat niet werken. (Zo’n modern toestel heeft waarschijnlijk de -48VDC nodig en zal misschien ook onblij worden van de DC component in het belsignaal. Ik weet niet of het stuk gaat, maar ga er maar van uit dat het op zijn minst Niet Werkt en ook wel eens Stuk Zou Kunnen Gaan. Niet mij boos opbellen als je telefoon stuk gegaan is, dus. Dit is een kort afgesneden bocht. U bent gewaarschuwd.)

Don’t try this at home

Probeer dit dus niet op een modernere telefoon. Of op zijn minst: wordt niet kwaad als ‘ie stuk gaat. Misschien nog beter: Probeer dit geheel niet. En wordt überhaupt niet kwaad. En ik ga er van uit dat als je dit werkend krijgt, je ook het benul hebt om van spanningsvoerende delen af te blijven.

Het voor het 20Hz gedeelte van het filmpje gebruikte schema staat hierboven (maar er is een verbetering mogelijk, zie verderop).

Met een ietwat aangepaste step-up uit de vorige blogpost maak ik een DC spanning van ongeveer 150 V. Met een tweede PWM kanaal van de microcontroller wordt een PWM signaal gemaakt, waarbij de pulsbreedte uit een sinus-tabel wordt gehaald. De sourcecode staat op github: Sourcecode van telefoonrinkelaar. Het 5Vtt PWM signaal uit de microcontroller wordt door Q2,Q3,Q4 niveau-vertaald tot een ongeveer 150 Vtt PWM signaal, R5 en C3 vormen een laagdoorlaatfilter. Op de basis van Q5 wordt zo een ongeveer sinusvormig signaal aangeboden. Q5 dient als emittervolger / bufferversterker. R6 dient als bleederweerstand voor de condensator in de T65 en wordt dus rechtstreeks met de telefoon verbonden.

De belspanning is ongeveer 110 Vtt onbelast. Tijdens het rinkelen van de telefoon is het ongeveer 50 Vtt met 60V DC offset. Op de oscilloscope ziet dat er als volgt uit:

Het lijkt in de verte wel op een sinus, maar bij belasting gebeurt er wat raars.

Hoe kan het beter

Via emittervolger Q5 kan alleen stroom worden geleverd aan de bel in te telefoon, en geen stroom worden opgenomen. Als een extra transistor (Q6) wordt toegevoegd kan er ook stroom opgenomen worden en blijft de golfvorm tijdens het rinkelen een stuk mooier:

De onbelaste spanning blijft gelijk.

Q6 gaat geleiden als de spanning op de emitter (de belspanning die aan de telefoon wordt aangeboden) hoger is dan de spanning op de basis. In dat geval dient R7 als extra belasting van de uitgang (belspanning), die dus stroom kan opnemen. Zo kan de uitgangsspanning weer omlaag, ook als daar een inductieve of capacitieve last op is aangesloten.

Om de sinusvorm sinusvormiger te maken, zou je de PWM frequentie kunnen verhogen en/of een beter filter kunnen toepassen. (Let wel op dat het in dat topic over andere frequenties gaat).

Hoe kan het slechter

Als je nog een bocht verder afsnijd, kan het ook met de sourcecode uit deze andere git gist. Het schema wordt dan simpeler:

De ‘sinus’ voor de belspanning wordt dan gemaakt door het setpoint van de schakelende regelaar te veranderen, waar ik het in die vorige blogpost over had, maar zelfs op een relatief trage 20 Hz wil dat niet mooi werken. Omdat de voeding niet kan sinken (alleen stroom kan leveren en niet kan opnemen) rinkelt de telefoon ook niet. Als een extra last wordt toegevoegd rinkelt de telefoon wel, ik gebruikte daarvoor een neonlampje. Maar de golfvorm wordt extreem lelijk. Zie onderstaande oscillogrammen.

En waarschijnlijk wil die telefoon op een blokgolf ook wel rinkelen, maar het ging me hier juist om een demonstratie van het PWM-en van een sinus.

Sinustabel

De sinustabel komt in beide gevallen uit dit stukje python, al zijn er ook online tooltjes te vinden om sinus tabellen te maken (zoekterm: Sine LUT).

Overige Opmerking(en)

Je zou hiermee ook op een heel ingewikkelde manier beide elektroden van een neonlampje kunnen laten oplichten (schema), maar dat kan ook op een eenvoudiger manier. Meer daarover in een volgende post.

Wie hier eerder iets gelezen heeft zal niet verrast zijn als ik zeg dat ik iets heb met nixiebuisjes, neonlampjes en switched-mode step-up converters (boostconverters). Naast andere dingen, maar dit wordt een technische post.

In mijn (Xantus kit) nixieklok zit een boostconverter rondom een NE555. Maar, er zit ook een microcontroller in die de klok bestuurt. Een boostconverter kan ook gemaakt worden met de ADC en 1 van de PWM kanalen van een microcontroller. Behalve voor nixieklokken en andere toepassingen met nixiebuisjes of neonlampjes is zo’n boost converter voor tal van andere dingen bruikbaar. Er is in software controle over de uitgangsspanning. Bijvoorbeeld in mijn jongleerkubussen pas ik een dergelijke step-up toe als leddriver, daarbij wordt de uitgangsstroom gereguleerd en gestuurd om verschillende LED kleuren te mengen. (ipv een spanningsdeler een shunt in de terugkoppeling, zie schema).

Een schema van zo’n step-up voor een nixieklok ziet er ongeveer zo uit:

Even in verband met die 250V: Ik ga er van uit dat wanneer je dit kunt nabouwen, je ook het benul hebt om van spanningvoerende delen af te blijven. (Uiteraard kun je ook hogere of lagere spanningen maken, waar zo nodig ook afgebleven moet worden).

De microcontroller is uit dit schema weggelaten, elk type met een ADC ingang en een PWM uitgang is geschikt. Ik gebruik een totempole driver om de gate van de IRF740 aan te sturen, omdat de microcontroller waar ik mee test op 3,3 V loopt. Om de FET goed open te sturen is een hogere gatespanning nodig. (De gate Threshold van een IRF740 is minimaal 2 V en maximaal 4 V.)

Ik heb er mee gespeeld in STM32CubeMX met een STM32F030, maar dat geeft zo’n berg autogegenereerde code en commentaar, dat het idee beter over te brengen is met een stukje pseudocode. Hieronder eerst een omschrijving hoe het in CubeMX werkt en verderop pseudocode waarmee het idee beter uit te leggen is. Daarna nog een keertje op een ATmega328. Dat is hopelijk wel genoeg.

STM32F030 en STM32CubeMX

In de CubeMX omgeving kan TIM1 worden ingesteld op ‘output compare no output’ (screenshot) en de ADC worden ingesteld om te triggeren op ‘timer 1 trigger out event’ (screenshot). Zo triggert TIM1 steeds een ADC conversie zodat de ADC op een vaste samplerate sampled. Timer3 wordt gebruikt voor PWM output (en eveneens ingesteld in de configuratortool).

De timers worden geklokt uit 24 MHz, TIM3 telt tot 240 zodat 100 kHz PWM gedaan wordt. TIM1 telt tot 2400 zodat de ADC op 10 kHz triggert. Vervolgens worden in main de ADC en de timers gestart met:

  HAL_ADC_Start_IT(&hadc);    // start ADC so it can be triggered by timer
  HAL_TIM_Base_Start(&htim1); // start the timer that triggers the ADC to convert
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // start the timer for the PWM output

Vervolgens kan in de interruptcode de ADC interrupt worden ingesteld om de PWM-timer aan te sturen: (En te clampen op een maximale dutycycle van ergens tussen de 50% en de 75% om te voorkomen dat de spoel verzadigd)

void ADC1_IRQHandler(void)
{
  /* USER CODE BEGIN ADC1_IRQn 0 */
    uint32_t raw_adc_input;
    int errorvalue;
    if(LL_ADC_IsActiveFlag_EOC(ADC1)){
        raw_adc_input = HAL_ADC_GetValue(&hadc);
	errorvalue = 1241 - raw_adc_input; // err = soll - ist, using 1V reference voltage
	if(errorvalue>0){
	    //TIM3_OC1A = ... via hal
	    if(errorvalue<168){ // clamp to 168/240= 70% ducy
	        __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,errorvalue);
	    } else
                __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,168);
	}else __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,0);
        //HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // for debug, output half of ADC trigger frequency.
    }
  /* USER CODE END ADC1_IRQn 0 */
  HAL_ADC_IRQHandler(&hadc);
  /* USER CODE BEGIN ADC1_IRQn 1 */
  /* USER CODE END ADC1_IRQn 1 */
}

Een spanningsdeler deelt de gewenste uitgangsspanning af naar 1 V referentiespanning, aangeboden aan de ADC.

Platformonafhankelijke Pseudocode met uitleg

Om te beginnen heb je een PWM-uitgang en een ADC ingang nodig op een microcontroller naar smaak. De ADC moet op een vaste samplerate samplen, althans, het reguleren van de uitgang wordt onvoorspelbaar als de samplerate niet vast is. Op een STM32F0 kun je een timer rechtstreeks (hardwarematig) een ADC conversie(sessie) laten starten, op een andere MCU zit er mogelijk wat software tussen maar kan dat ook.

De pseudocode hieronder toont hoe de step-up werkt: Op een vaste samplerate neemt de ADC samples. Deze worden vergeleken met een ingesteld setpoint. Als de gewenste uitgangsspanning nog niet bereikt is, wordt er een PWM signaal uitgestuurd naar de FET welke de spoel schakelt. Als de uitgangsspanning wel bereikt is, wordt het PWM signaal 0 en de FET niet meer aangestuurd. Daartussen varieert het PWM signaal naar rato. Het PWM signaal wordt beperkt tot maximaal bijvoorbeeld 70% dutycycle, omdat bij 100% dutycycle de FET constant aan staat en dus de voeding kortsluit, en omdat de spoel kan verzadigen op een ‘te hoge’ dutycycle.

/* pseudo-code voor softwarematige step-up SMPS */
#define SETPOINT 512  // overeenkomend met b.v. 1 volt in ADC ticks

// ADC sampled op 10 kHz, getriggert door een timer (TIMER 1)
// steeds als een conversie gedaan is, loop onderstaande routine
// TIMER 2 is ingesteld om PWM uit te sturen
ADC_ISR(){
  int rawsample, dutycycle;
  rawsample = ADC_RESULT;            // lees conversie-resultaat
  dutycycle = SETPOINT - rawsample; 
  // naarmate het setpoint bereikt wordt, neemt dutycycle af
  
  if(dutycycle<0) dutycycle = 0;     // clamping tegen 0
  if(dutycycle>MAX) dutycycle = MAX; // clamping tegen maximum (b.v. 70%)
  TIMER2_PWM = dutycycle;            // stel dutycycle in
}

Er is dan een spanningsdeler die de uitgangsspanning afdeelt naar het ingestelde SETPOINT.

Met een Atmega328

Omdat MPLAB X toch fijner werkt dan STM32CubeMX ook nog maar een keertje in een Atmega328. Het setpoint van 128 komt hier overeen met 0,55 V: de interne 1.1 V ADC referentie wordt gebruikt.

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 8e6
#include <util/delay.h>

int16_t setpoint = 128; //With 1.1V internal ADC reference and a 8 bit result, 128 means 0.55 V approximately for the output divider

void setup()
{
    /*GPIO*/
    DDRC = 0xBE;                  // PC0 input, PC6=reset input, rest ouput
    DDRB = 0x3F;                  // all output, except crystal input PB6,7
    DDRD = 0xF7;                  // all output, except PD3 (switch?)
 
    /* Timer 1, for PWM output and ADC samplerate (PWM /8) 
     * Has to be set to inverted output, because otherwise OCRA1 == 0 results in a small spike
     * on inverted, OCR1A == TOP == ICR1 means a constantly low signal and OCR1A = 0 a nearly constantly high signal
     */
    ICR1 = 80;                    // use 80 as top, so 8M/80=100 kHz PWM
    TCCR1A = 0b11000010;          // use OC1A output (inverted), fast PWM, ICR1 as top
    TCCR1B = 0b00011001;          // WGM mode fast PWM ICR1 as top, clock timer from clkIO unprescaled, start
    TIMSK1 = (1<<TOIE0);          // enable interrupt on overflow (at ICR1 value)
  
    /*ADC*/
    ADMUX = 0b11100000;           // set reference voltage to 1V1 internal, use ADC0 as input, left adjust result (so only higher 8 bits have to be read)
    ADCSRA = (1<<ADEN)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS0); // ADC enable, interrupt enable, clock source clkio/32 (8M/32 < 200kHz) 
    DIDR0 = 0x01;                 // disable digital input buffer on ADC0 input   
    sei();                        // enable interrupts
}

int main(void) {
    setup();
    while (1) {

    }
}

ISR(TIMER1_OVF_vect){
    static uint8_t i=0;
    i++;
    if(i>=7){ 
     ADCSRA |= (1<<ADSC); // start a new adc conversion every 8th OVF. OVF at 100 kHz, ADC max is 15ksps at max resolution
     i=0;
    }
    PIND = 0x01; // toggle PD0 for debug
}

ISR(ADC_vect){
    /* 
     * read adc result, and set new PWM value
     * because PWM is inverted to prevent the 1 cycle high output
     * 80 means output LOW, 0 means 100% dutycycle high.
     * 
     * If PWM were not inverted an OCR1A 0 would mean 0 output instead of
     *  a thin needle of 1 clk cycle, then this would be a bit simpler:
     * if error > 0 {if error<56 OCR=error }else ocr=56 else ocr = 0;
     */
    uint8_t adcresult = ADCH; 
    int16_t error = (setpoint - adcresult)*8; // P factor
    if(error>0){
        if(error < 57) OCR1A = 80-error; else OCR1A = 40;  // limit to 50% dutycycle (40) max
    }else OCR1A = 80;
}

En Wat Is Daar Het Praktisch Nut Van?

Ik hoop dat dit voor een mede-electronicus misschien iets zinnigs / nuttigs bevat, bijvoorbeeld als je een project met een microcontroller hebt waar ook een paar hogere spanningen nodig zijn, bijvoorbeeld iets met nixies of VFD-buisjes of iets om EEPROMS te wissen ofzo. (Of je zet er nog een lineaire regelaar achter als de regulatie wat preciezer moet zijn omdat je iets wilt met een e-ink display, bijvoorbeeld)

Maar ook: in een volgende blogpost pas ik een dergelijke boost converter toe, en wil ik graag dat er een belletje gaat rinkelen of een lampje gaat branden.