Ceas cu afisaj VFD

Ca o prima postare am ales un proiect mai “aratos”: un ceas cu afisaj VFD

Pentru afisaj am folosit un tub IV-18:

IV-18 tube

IV-18 tube

Caracteristicile tubului sunt:

  • 9 digiti, 8 cu 7 segmente si virgula, 1 cu simboluri speciale.
  • tensiune de filament : 5V la 85mA
  • tensiune anodica: 20-30V
  • curent de grila: 11mA per digit
  • curent anodic: 8mA per digit, cu toti anozii aprinsi

Mai multe informatii puteti gasi aici

Design

  • Microcontroller: un CORTEX-M3
  • Real time clock: de preferat integrat in microcontroller
  • Sursa de tensiune anodica: cat mai simpla folosind un integrat dedicat
  • Dimensiunea pcb-ului: pe latura lunga sa nu depaseasca lungimea tubului

Microcontroller

Cerintele de baza ale microcontroller-ului au fost costul, RTC-ul si battery backed-up RAM integrate, prezenta interfetei JTAG si utilizarea unui integrat dintr-o serie neutilizata in alte proiecte.

Am ales STM32F100R6T6, un chip fabricat de ST, capsula TQFP64, 8K ram si 32K flash.
Frecventa maxima este de 24Mhz.

STM32F100R6

CPU

JTAG

In jurul procesorului am folosit componentele minime pentru a-l utiliza, conform manualului: quartz-uri pentru system clock si RTC clock, condensatori de decuplare pe liniile de alimentare si de reset si rezistente de pull-down pe pinii de boot mode select.

Conectorul de JTAG este unul clasic, header 2×10 pini, 2.54mm.

Microcontroller-ul are incluse rezistentele de pull-up / pull- down pe toate liniile si nu a mai fost nevoie de rezistente suplimentare. Am pastrat doar cele 3 rezistente de pull-down pe liniile neutilizare ( R20, R21 si R22 10K) si R34 pentru limitare de curent.
Sunt utilizati aproape toti pinii de I/O disponibili. Am lasat un port serial cu un header de 2.54mm ( UART2 ) si doua semnale analogice ( AMB_IN si TEMP_IN ) pentru o extensie ulterioara.

Bateria de back-up este de tip CR2032, cu un conector SMD lipit pe PCB.

SMPS

Tensiunea pentru anozii tubului este generata cu un BOOST clasic, comandat de procesor. Am ales un tranzistor MOSFET cu Vth mic ce poate fi direct comandat de un canal de PWM. Rezistenta R3 de 10 ohmi are doua roluri: formeaza un filtru RC cu C5, C6 si condensatorii de filtrare de pe linia de alimentare ( VCC_RAW ) si are rolul de limitare a curentului / siguranta fuzibila in cazul in care tranzistorul Q1 ramane deschis. Tensiunea de feed-back este preluata printr-un divizor rezistiv format din R6,R4 si filtrata apoi cu R5 si C7

Cateva forme de unda:

VgQ1

Frecventa de comanda este de aproximativ 93.75KHz – PWM clock este 24Mhz, 8 biti rezolutie.
Factorul de umplere este de ~ 40% pentru luminozitate 5/10, ce se traduce in aproape 30V la iesire.

VdsQ1

Aici se vede forma de unda in nodul de comutatie: tensiunea de fly-back si oscilatia libera in perioada in care tranzistorul este deschis

VgQ1_detail

Datorita curentului mic de comanda se pot vedea efectele capacitatiilor parazite: panta fronturilor crescatoare / descrescatoare si oscilatiile parazite transmise prin capacitatea drena – poarta

Vnoise

Tensiunea de iesire are un riplu destul de mare datorita numarului redus de pasi de PWM ( 256 ) si a vitezei mici a buclei de reactie

Driverele

driver

Dupa mai multe variante am ales driverele integrate ULN2803 – 8 canale open collector. Dezavantajul principal este dat de consumul inutil de energie: in cazul in care tubul este complet stins irosesc ~ 19x(50/100K) : 10mA sau ~ 0.5W. In functionarea normala se intalnesc rar astfel de cazuri.

 

Driverele open-emitter ar fi fost ideale, dar nu am gasit nimic pentru tensiunea respectiva, si suprafata necesara realizarii cu componente discrete ar fi fost mult mai mare. Alaturi de cele 2 drivere integrate ce imi asigura 16 canale mai exista un driver discret pentru canalul 17 – grila simbolurilor speciale.

Alimentare

bloc de alimentare

Alimentarea consta in 2 regulatoare lineare inseriate.
Primul coboara tensiunea de intrare la 5V ce ajunge la filament printr-o rezistenta de limitare si la stabilizatorul de 3.3V pentru microcontroller.
Din linia de 3.3V este alimentat si blocul analogic printr-un filtru RC.

PCB

PCB-ul este realizat la un producator dedicat si are 100x50mm. Materialul este FR4 de 1.6mm grosime, cupru de 35 microni. Via-urile sunt metalizate. Toata placa a primit un tratament HAL ( probabil stanare ).
Pentru a reduce costurile am renuntat la silkscreen si soldermask.

top layer

bottom layer

Se poate observa planul de masa continuu ce are si rolul de radiator pentru stabilizatorul de 5V.
PCB-ul a fost impartit in 4 “sectiuni”:

  • Alimentare de joasa tensiune: conectorul de alimentare, condensatorii de decuplare, stabilizatorul de 5 si 3.3V
  • Microcontroller-ul si ridicatorul de tensiune
  • Driverele pentru tub si conectorul acestuia
  • Butoanele interfetei cu utilizatorul

Impartirea a fost facuta tinand cont de componentele ce disipa multa caldura – stabilizatoarele liniare izolate in partea de sus a imaginii, de regulile de lay-out pentru sursele in comutatie – traseele ce poata curenti mari au dimensiuni foarte mici, cu exceptia planului de racire conectat la radiatorul lui Q1.
Asamblarea a fost facuta de mana, cu ajutorul unui microscop.
Lipsa solder mask-ului a pus probleme – cositorul avand tendinta sa se intinda pe planul de masa.

 

Firmware

Firmware-ul este scris exclusiv in C, folosind compilatorul gcc si un toolchain bazat pe Eclipse si OpenOCD.
Codul este alcatuit din patru bucle:

  • ADC ISR ce face medierea tensiunii de feedback si controleaza duty cycle-ul regulatorului boost
  • TIM6 ISR : multiplexarea segmentele de afisaz
  • TIM7 ISR : citirea tastelor si debounce
  • bucla infinita in main, ce controleaza interfata cu utilizatorul

ADC

Controller-ul ADC este configurat sa citeasca succesiv 3 canale – tensiunea de feedback si cele 2 intrari libere.
La fiecare conversie valorile citite sunt mediate cu valorile precedente apoi se stabileste noul duty cycle printr-un comparator simplu.
Duty cycle-ul este updatat la cateva mii de valori citite, pentru a reduce viteza buclei de reactie

if ( 1000 == adcISRState)
{
adcISRState = 0;
/* update the duty cycle */
if ( adcDCDCMed > adcDCDCSetPoint )
{
adcDCDCPwmDuty --;
}
else
{
adcDCDCPwmDuty ++;
}
pwmSetDutyDCDC(adcDCDCPwmDuty);
}
else
{
adcDCDCMed = (adcDCDCMed + adcDCDC) >> 1 ;
}

Zgomotul din poza “Vnoise” se datoreaza in mare parte acestui algoritm simplificat.

TIM6

Timer-ul TIM6 este configurat sa genereze o intrerupere cu o perioada de ~ 1mS.
La fiecare “tick” este aprinsa urmatoarea grila din afisaj. Comutarea este de genul “break before make” penrtru a reduce efectul de “ghosting”: sinbolul curent este stins inainte de a se trece la urmatorul.

void __attribute__((__interrupt__)) timer6ISR(void)
{
        unsigned int status = TIM6->SR;
        TIM6->SR = ~ status; /* clear bits set to 1 */
        if ( (TIM_SR_UIF&status) != 0 )
        {
                /* my isr */
                /* time to display character * */
                /* grids start from PA4 - PA12 */
                /* segments from PC4 to PC11 */
                /* set all grids and lines to 1 - display off */
                GPIOA->BSRR = GPIO_BSRR_BS4 | GPIO_BSRR_BS5 | GPIO_BSRR_BS6 | GPIO_BSRR_BS7 | GPIO_BSRR_BS8 | GPIO_BSRR_BS9
                                        | GPIO_BSRR_BS10 | GPIO_BSRR_BS11 | GPIO_BSRR_BS12;
                GPIOC->BSRR = GPIO_BSRR_BS4 | GPIO_BSRR_BS5 | GPIO_BSRR_BS6 | GPIO_BSRR_BS7 | GPIO_BSRR_BS8 | GPIO_BSRR_BS9
                                        | GPIO_BSRR_BS10 | GPIO_BSRR_BS11 ;

                /* enable the segment */
                GPIOC->BSRR = displayBuffer[currentChar] << 20;
                /* enable the grid */
                GPIOA->BSRR = GPIO_BSRR_BR4 << currentChar;
                /* next char */
                currentChar ++;
                if ( currentChar > 8 ) currentChar = 0;
        }
}

Codul de control al afisajului mai contine si functii ajutatoare ce mapeaza caracterele ascii pe font-ul intern, aprinde virgula fiecarui segment si controleaza simbolurile speciale.

TIM7

Timer-ul TIM7 se ocupa cu scanarea tastaturii, filtrarea apasarilor prea scurte si notificarea apasarii unui buton.

Filtrarea este simpla si eficienta: la fiecare tick se verifica daca tasta este apasata. Daca da, contorul de debounce se incrementeaza cu 1 pana cand atinge valoare de prag ce genereaza o notificare. Daca tasta nu este apasata contorul de debounce se duce in 0.

if ( (keys & (1<<12 )) == 0 )
{
    if ( UI_DEBOUNCE_CONSTANT != keyDebounce[0]) keyDebounce[0] += 1;
}
else
{
    keyDebounce[0] = 0;
}
....
if (  UI_DEBOUNCE_CONSTANT == keyDebounce[0])
{
    keyCodes |= UI_KEY_UP;
    keyDebounce[0]=0;
}

Bucla din main

Bucla principala de program controleaza interfata cu utilizator: ce informatii se afiseaza pe display si navigarea prin meniurile de configurare / functii auxiliare.

TO DO

Urmatoarele functii sunt in continuare in lucru:

  • Salvarea datelor de configurare in back-up RAM
  • Alarma – configurare si afisare alarme
  • Count down timer
  • Infrastructura de control a unor viitoare periferice atasate pe UART2
  • Imbunatatire algoritm de control pentru sursa in comutatie
  • Corectie PCB – 2-3 footprint-uri gresite

 

Galerie foto:

[imagebrowser id=1]

Category: Proiecte | Tags: , , ,
Comments are disabled