Skocz do zawartości
Pixelx

Duszki NIXIE SN74141 - multipleksowanie

Pomocna odpowiedź

Napisano (edytowany)

Witam.
Buduję zegar na lampach NIXIE IN12B, uC to Atmega32 oraz dekoder BCD SN74141, napięcia zasilania lamp 180V.
Wszystko działa na multipleksowaniu. Problem też znany gdyż chodzi tu o tzw ,,duszki".
Szukałem sprzętowych rozwiązań i znalazłem takie z diodami i nie mam pojęcia czemu to nie działa, być może przez to ze używam tego dekodera, gdyż na innych schematach w internecie ludzie używali do załączania poszczególnych cyfr tranzystorów wysokonapięciowych.
Pytanie jak rozwiązać ten problem u mnie? Prosiłbym o pomoc.

Kawałek schematu z internetu podłączone mam identycznie jak tu i dodałem do każdej linii dekodera diodę podłączoną anodą do wyprowadzenia z każdego pinu. Diody jakie zastosowałem to IN5819 kondensator 200nF.
Jeżeli podłączyłem jak na schemacie wraz z tym rezystorem 100kohm to wszystkie cyfry mi się paliły, próbowałem różne kombinacje z tym robić i ciągle widać duszki.

schemat.png

schemat2.PNG

Edytowano przez Pixelx

Udostępnij ten post


Link to post
Share on other sites
(edytowany)
11 godzin temu, Pixelx napisał:

próbowałem różne kombinacje z tym robić i ciągle widać duszki.

Schemat to jedno, istotny jest również algorytm multipleksowania. Lampy NIXIE mają dużo większą bezwładność (czas gaśnięcia cyfry po odłączeniu napięcia sterującego) w porównaniu z LED, dlatego należałoby to uwzględnić w kodzie. Kodu nie przedstawiłeś, więc trudno się wypowiedzieć, czy Twój algorytm multipleksujący jest odpowiedni. Zwykle multipleksowanie odbywa się cyklicznie w procedurze obsługi przerwania jakiegoś timera, więc gdybyś mógł przedstawić chociaż ten fragment kodu, może udałoby się coś podpowiedzieć.

Myślę, że przy prawidłowym algorytmie multipleksowania kombinowanie z diodami może okazać się zbędne.

Edytowano przez andrews

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Do programu przygotowałem sobie bibliotekę więc przedstawię te pliki. Częstotliwość to tak gdzieś 360Hz? Mam 6 lamp
8Mhz taktowanie uC na wewnętrznym rezonatorze RC.

W takiej kolejności są kody przedstawione.

main.c
nixie.c
nixie.h

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "NIXIE/nixie.h"
#include "I2C_TWI/i2c_twi.h"

#define DS3231_ADDR 0xD0

uint8_t bcd2dec(uint8_t bcd);
uint8_t dec2bcd(uint8_t dec);
void DS3231_set_time( uint8_t hh, uint8_t mm, uint8_t ss );
void DS3231_init();

int main(void)
{

    nixie_init();
    DS3231_init();

    PORTD |= (1<<PD2);                // konfiguracja wejścia INT0
    MCUCR |= (1<<ISC01)|(1<<ISC00); // zbocze narastające
    //DS3231_set_time( 13,52,30 );
    i2cSetBitrate(100);

    enum {ss, mm, hh};
    enum {cel, fract };
    uint8_t bufor[3];        
    uint8_t sekundy, minuty, godziny;
    uint8_t sekundy_ob1, sekundy_ob2, minuty_ob1, minuty_ob2, godziny_ob1, godziny_ob2;

    //sekunda1=3;


    sei();


        while(1)
        {

            if( GIFR & (1<<INTF0) )
            {

                TWI_read_buf( DS3231_ADDR, 0x00, 3, bufor );
                godziny = bcd2dec( bufor[hh] );
                minuty = bcd2dec( bufor[mm] );
                sekundy = bcd2dec( bufor[ss] );


                godziny_ob1 = godziny / 10;
                godziny_ob2 = godziny % 10;

                minuty_ob1 = minuty / 10;
                minuty_ob2 = minuty % 10;

                sekundy_ob1 = sekundy / 10;
                sekundy_ob2 = sekundy % 10;

                godzina1 = godziny_ob1;
                godzina2 = godziny_ob2;

                minuta1 = minuty_ob1;
                minuta2 = minuty_ob2;

                sekunda1 = sekundy_ob1;
                sekunda2 = sekundy_ob2;


                GIFR |= (1<<INTF0);  
            }


        }
}


void DS3231_init( void ){
    uint8_t ctrl = 0;
    TWI_write_buf( DS3231_ADDR, 0x0e, 1, &ctrl );
}

void DS3231_set_time( uint8_t hh, uint8_t mm, uint8_t ss ) {
    uint8_t buf[3];
    buf[0]=dec2bcd(ss);
    buf[1]=dec2bcd(mm);
    buf[2]=dec2bcd(hh);
    TWI_write_buf( DS3231_ADDR, 0x00, 3, buf );
}


// konwersja liczby dziesiętnej na BCD
uint8_t dec2bcd(uint8_t dec){
return ((dec / 10)<<4) | (dec % 10);
}

// konwersja liczby BCD na dziesiętną
uint8_t bcd2dec(uint8_t bcd){
    return ((((bcd) >> 4) & 0x0F) * 10) + ((bcd) & 0x0F);
}
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "nixie.h"

volatile uint8_t godzina1;
volatile uint8_t godzina2;
volatile uint8_t minuta1;
volatile uint8_t minuta2;
volatile uint8_t sekunda1;
volatile uint8_t sekunda2;


const uint8_t cyfry[10] PROGMEM = {
        ~(A0|A1|A2|A3), //0
        (A0),            //1
        (A1),            //2
        (A0|A1),         //3
        (A2),            //4
        (A0|A2),        //5
        (A1|A2),        //6
        (A0|A1|A2),        //7
        (A3),            //8
        (A0|A3)            //9
};


void nixie_init(void){
    NIXIE_DATA_DIR = 0xFF;                                                       
    NIXIE_DATA = 0xFF;                                                        
    ANODY_DIR |= ANODA1 | ANODA2 | ANODA3 | ANODA4 | ANODA5 | ANODA6;        
    ANODY_PORT |= ANODA1 | ANODA2 | ANODA3  | ANODA4 |ANODA5 | ANODA6;    


    TCCR0 |= (1<<WGM01);                // tryb CTC
    TCCR0 |= (1<<CS02)|(1<<CS00);        // preskaler 1024
    OCR0 = 22;
    TIMSK |= (1<<OCIE0);            
}


ISR(TIMER0_COMP_vect){
    static uint8_t licznik=1;

    ANODY_PORT = (ANODY_PORT | MASKA_ANODY);                                    // wygaszenie wszystkich wyświetlaczy;


    if(licznik==1)         NIXIE_DATA = pgm_read_byte( &cyfry[godzina1] );
    else if(licznik==2) NIXIE_DATA = pgm_read_byte( &cyfry[godzina2] );
    else if(licznik==4) NIXIE_DATA = pgm_read_byte( &cyfry[minuta1] );
    else if(licznik==8) NIXIE_DATA = pgm_read_byte( &cyfry[minuta2] );
    else if(licznik==16) NIXIE_DATA = pgm_read_byte( &cyfry[sekunda1] );
    else if(licznik==32) NIXIE_DATA = pgm_read_byte( &cyfry[sekunda2] );

    ANODY_PORT = (ANODY_PORT & ~MASKA_ANODY) | (licznik & MASKA_ANODY);            // cykliczne przełączanie kolejnej anody


    licznik <<= 1;
    if(licznik>32) licznik = 1;
}

 

#ifndef nixie_h        
#define nixie_h


#define     NIXIE_DATA     PORTA
#define        NIXIE_DATA_DIR DDRA
#define     ANODY_PORT     PORTB
#define     ANODY_DIR     DDRB


#define     ANODA1     (1<<PB0)
#define     ANODA2     (1<<PB1)
#define     ANODA3     (1<<PB2)
#define     ANODA4     (1<<PB3)
#define     ANODA5     (1<<PB4)
#define     ANODA6     (1<<PB5)

#define MASKA_ANODY (ANODA1|ANODA2|ANODA3|ANODA4|ANODA5|ANODA6)

// definicje bitów dla 74147
#define A0 (1<<0)
#define A1 (1<<1)
#define A2 (1<<2)
#define A3 (1<<3)

extern volatile uint8_t godzina1;
extern volatile uint8_t godzina2;
extern volatile uint8_t minuta1;
extern volatile uint8_t minuta2;
extern volatile uint8_t sekunda1;
extern volatile uint8_t sekunda2;

void nixie_init(void);

#endif    


 

Edytowano przez Pixelx

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

 

Moim zdaniem w przypadku multipleksowania nixie lepszym sposobem jest naprzemienne gaszenie i zapalanie kolejnych cyfr. Inaczej to ujmując, w nieparzystych przerwaniach gasisz wszystko (wyłączasz wszystkie anody i katody), a w parzystych zapalasz kolejne cyfry.

Przykładowa procedura obsługi przerwania (starałem się zbyt wiele nie zmieniać, więc może nie być optymalnie, ale powinno działać):

ISR(TIMER0_COMP_vect)
{
    static uint8_t anoda = 1, licznik;
    
    licznik ^= 1;
    
    if (licznik)
    {
        // wygaszenie wszystkich wyświetlaczy;
        ANODY_PORT = (ANODY_PORT | MASKA_ANODY);
        NIXIE_DATA = 0xFF;
    }
    else
    {
        // cykliczne przełączanie kolejnej anody
        ANODY_PORT = (ANODY_PORT & ~MASKA_ANODY) | (anoda & MASKA_ANODY);
        
        switch (anoda)
        {
            case 1:
                NIXIE_DATA = pgm_read_byte( &cyfry[godzina1] );
                break;
            case 2:
                NIXIE_DATA = pgm_read_byte( &cyfry[godzina2] );
                break;
            case 4:
                NIXIE_DATA = pgm_read_byte( &cyfry[minuta1] );
                break;
            case 8:
                NIXIE_DATA = pgm_read_byte( &cyfry[minuta2] );
                break;
            case 16:
                NIXIE_DATA = pgm_read_byte( &cyfry[sekunda1] );
                break;
            case 32:
                NIXIE_DATA = pgm_read_byte( &cyfry[sekunda2] );
                break;
            default:
                NIXIE_DATA = 0xFF;
                break;
        }
        
        anoda <<= 1;
    }
    
    if (anoda > 32)
    {
        anoda = 1;
    }    
}

 

Edytowano przez andrews

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Dodałem do projektu tą przeróbkę i kompiluje się wszystko. Nie mogę na razie powiedzieć czy to działa bo jestem poza domem już i to tak na 2 tyg.

 

Pytanie czy częstotliwość będzie dobra? Czy powinna być wyższa? Bo wtedy trzeba by zmienić rejestr porównania OCRx

Edytowano przez Pixelx

Udostępnij ten post


Link to post
Share on other sites

 

18 godzin temu, Pixelx napisał:

Pytanie czy częstotliwość będzie dobra? Czy powinna być wyższa? Bo wtedy trzeba by zmienić rejestr porównania OCRx

Jeśli chodzi o częstotliwość multipleksowania, to chyba trzeba będzie dobrać eksperymentalnie. Być może trzeba będzie nieco zwiększyć, ale nie przesadzałbym. Myślę, że maksymalnie około 600Hz powinno wystarczyć.

Istnieje jednak jeszcze inna możliwość. W przypadku multipleksowania wyświetlaczy LED wystarczy zgaszenie jednej cyfry i zapalenie w trakcie jednej procedury obsługi przerwania. W przypadku nixie jednak taki czas wygaszania cyfry może być niewystarczający. Rozwiązanie, które przedstawiłem powoduje, że ten czas wygaszenia jest wystarczająco długi, ale może niekoniecznie musi być aż tak długi. Właściwie powinien być tylko tak długi, na ile to konieczne, by uniknąć prześwitywania, oraz wystarczająco krótki, aby uniknąć ewentualnego efektu migotania. Dlatego dobrym rozwiązaniem byłoby skrócenie czasu wygaszenia, a wydłużenie czasu, kiedy cyfra świeci. Można to osiągnąć, zmieniając odpowiednio wartość rejestru OCR0 w trakcie obsługi przerwania, np.:

// wewnątrz procedury obsługi przerwnia można dodać linijki
    if (licznik)
    {
        // ustalenie czasu wygaszenia
        OCR0 = 2;
        // wygaszenie wszystkich wyświetlaczy;
        ANODY_PORT = (ANODY_PORT | MASKA_ANODY);
        NIXIE_DATA = 0xFF;
    }
    else
    {
        // ustalenie czasu świecenia
        OCR0 = 10;
        // cykliczne przełączanie kolejnej anody
        ANODY_PORT = (ANODY_PORT & ~MASKA_ANODY) | (anoda & MASKA_ANODY);
        // ... reszta kodu
    }

Wartości rejestru OCR0 są przykładowe, należy je dobrać eksperymentalnie, ale dzięki takiemu rozwiązaniu można uzyskać lepsze proporcje czasu świecenia do czasu wygaszenia.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Dziękuję bardzo za pomoc. Jak będę miał możliwość zaraz to przetestuję

Podsumowując przerwanie wygląda tak:

 

ISR(TIMER0_COMP_vect){



	static uint8_t anoda = 1, licznik;

	    licznik ^= 1;

	    if (licznik)
	    {
	    	OCR0=2;
	        // wygaszenie wszystkich wyświetlaczy;
	        ANODY_PORT = (ANODY_PORT | MASKA_ANODY);
	        NIXIE_DATA = 0xFF;
	    }
	    else
	    {
	        // cykliczne przełączanie kolejnej anody
	    	OCR0=10;
	        ANODY_PORT = (ANODY_PORT & ~MASKA_ANODY) | (anoda & MASKA_ANODY);

	        switch (anoda)
	        {
	            case 1:
	                NIXIE_DATA = pgm_read_byte( &cyfry[godzina1] );
	                break;
	            case 2:
	                NIXIE_DATA = pgm_read_byte( &cyfry[godzina2] );
	                break;
	            case 4:
	                NIXIE_DATA = pgm_read_byte( &cyfry[minuta1] );
	                break;
	            case 8:
	                NIXIE_DATA = pgm_read_byte( &cyfry[minuta2] );
	                break;
	            case 16:
	                NIXIE_DATA = pgm_read_byte( &cyfry[sekunda1] );
	                break;
	            case 32:
	                NIXIE_DATA = pgm_read_byte( &cyfry[sekunda2] );
	                break;
	            default:
	                NIXIE_DATA = 0xFF;
	                break;
	        }

	        anoda <<= 1;
	    }

	    if (anoda > 32)
	    {
	        anoda = 1;
	    }
}

 

Edytowano przez Pixelx

Udostępnij ten post


Link to post
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »

×