Sono in difficolta con display 7 seg + 4 key

Ho un display 7 segmenti 4 cifre collegato come da schema parziale allegato, dove si vedono
le uscite da pin 23 a 26 che pilotano i bjt, ogni bjt è connesso al catodo comune di ogni cifra.
L’alta immagine mostra i 4 tasti, connessi direttamente ai pin 23 a 26 e una uscita tasti chiamata Kout connessa al pin 21 (PC2).

Il software chiama una funzione utente ogni 4.2 ms per aggiornare il display in multiplex, la funzione dovrebbe anche stabilire quali tasti vengono premuti e per quando tempo.

La funzione chiamata ogni 4.2ms è la seguente:

// chiama questa funzione periodicamente almeno ogni 4ms
// CATODO_NIBBLE = 4
// n_max_columns = 4, cioè display 7 seg 4 cifre.
//  uint8_t bit_buff[4], un array di interi da 4 elementi

void dled_update()
{
    static uint8_t idx = 0;

    PORTC &= ~0xf0;
    if (idx > n_max_columns-1)
        idx = 0;
    bit_idx = idx;
    PORTB = bit_buff[idx];
    PORTC |= _BV(idx + CATODO_NIBBLE);
    // qui devo aggiungere la decodifica dei tasti
}

Non riesco a focalizzare come agire, in base a quale logica, scrivere il codice.
Ogni 4.2 ms il valore di idx viene incrementato ma se supera 3 viene ripotato a 0 (zero)

Per stabilire quali tasti sono premuti devo almeno lasciare che dled_update venga chiamata almeno 4 volte, 4voltex4.2ms = 16.8 ms.
Le uscite del multiplex, si trovano tutte a 0, tranne quella del display in aggiornamento, quindi:
0001, 0010, 0100, 1000, 0001, 0100, 0100, 1000 e la cosa si ripete all’infinito.
Ad ogni bit dei 4 è connesso un tasto, se quel bit è acceso e il tasto è premuto il pin Kout da 0 passa ad 1. Ogni bit rimane alto per 4.2 ms, in questo tempo io non posso controllare costantemente lo stato del pin Kout, trascorsi 4.2ms Kout passa da 1 a 0 anche se il tasto rimane premuto, perchè quello che si spegne è il bit del multiplex, si spegne e si accende il successivo.

Adesso ho pensato che per le prima 4 chiamate alla routine devo salvare lo stato di Kout, e questa è già una cosa, ma poi cosa gli faccio fare al software, basta questo no devo anche rilevare il rilascio del tasto o dei tasti.

Spero di aver messo tutte le info necessarie a ricevere aiuto, o meglio uno spunto, un link o altri consigli sulla logica da adottare.

Ciao.

multiplex_key.png

multplex_out.png

sulla parte software sono inidoneo, invece dovresti aggiungere le R di base sui 4 transistor e prevedere comunque una R per ogni segmento; inoltre sarebbe cosa buona mettere anche una R tra base ed emettitore, in funzione di pull-down.

Si in effetti non si capisce dallo schema. Per limitare il numero di componenti ho usato dei transistor con R builtin, cioè le Rs e Rp sono interne al componente. Quindi se prendete lo schema come esempio per le vostre realizzazioni ricordare che se usate dei normali bjt dovete usare le Rs e Rp esterne.

Stessa cosa per la resistenza di degenerazione per ogni led del display, cioè dal segmento ‘a’ al segmento ‘dp’.

I diodi sui pulsanti non sono per niente opzionali, ci vanno anche se in altri schemi trovati in internet non sono presenti.

Per il codice sono alla frutta (quasi) perchè è evidente che ho un limitato angolo di visuale perchè per fare una cosa ci sono sempre modi diversi, io ne vedo solo uno e quindi qualcosa non torna.

Ciao. :wink:

Scusa, se ho capito bene hai collegato sia le uscite dei segnali di pilotaggio dei segmenti del display sia dei pulsanti agli stessi pin.
Ogni 4,2 ms tu accendi una cifra del display dando il segnale sul corrispondente pin. Se l'utente nel contempo preme quel pulsante, tu hai uno stato alto anche sul pin Kout.

Non ti basta all'interno della stessa routine di accensione dei led controllare se il pin Kout è alto? Se è alto, basta prendere idx come valore del pulsante premuto dato che sai per certo che solo il pulsante sul pin sotto tensione può aver dato il segnale alto al pin Kout.

Si, leo, intanto grazie per l’intervento.

Avevo difficoltà incredibile a organizzare il codice, sbagliavo approccio, pensavo ad ottimizzare e non ne usciva nulla di buono. Non riuscivo ad immaginare il funzionamento complessivo, ora ho scritto codice che sembra funzionare correttamente.

Inizialmente ho abilitato l’interrupt sul pin Kout, con risultati disastrosi, perchè la routine viene chiamata ogni 4.2ms se tengo il pulsante premuto, e il display sfrarfalla, quindi ho abbandonato la strada dell’interrupt.

Non basta rilevare la pressione, del pulsante, perchè il rilevamento avviene ogni 4.2ms e può trattarsi di un impulso spurio, il che è altamente probabile perchè il micro e i tasti si trovano a 3cm da 3 relè, di cui uno comanda un compressore frigorifero che è un carico fortemente induttivo. Inoltre non basta perchè serve rilevare la combinazione dei tasti e il tempo durante il quale il (o la combinazione di tasti) tasto rimane premuto.

Alla fine il codice è basato sugli stati sequenziali mutuamente esclusivi. Il codice seguente è quello che viene eseguito ogni 4.2ms:

// esegue lo scan se key_state == START_SCAN
if (key_state == STATE_START_SCAN) { // lo scanning può essere interrotto
// setta il bit CATODO_NIBBLE -idx, al valore letto da PC2 a cui è connesso l’uscita dei tasti Kout.
// i bit vengono scritti nella variabile internar_old_state_key (old è da togliere)
__bit_set_to(&internal_old_state_key_code, CATODO_NIBBLE - idx, PINC & _BV(PC2));

if (n_cycle == end_scan_value) { // end_scan_value vale al momento 23, valore trovato empiricamente
key_state = STATE_END_SCAN; // dopo aver letto 23 volte il pin Kout mette in pausa lo scanning
n_cycle = 0; // azzera anche il contatore di cicli di scanning
} else
n_cycle++; // incrementa il contatore solo se non è disabilitato lo scanning
} else { // sono trascorsi 23 n_cycle e lo scanning è sospeso
if (key_out.state == KEY_RELEASED) { // Lo stato dei tasti è inizialmente RELEASED
if (internal_old_state_key_code != 0) { // valuta il valore ricavato dallo scanning
key_out.state = KEY_PRESSED; // se è diverso da 0 allora uno dei tasti è premuto
key_out.time = 0; // il tempo di pressione parte da 0
key_out.key = internal_old_state_key_code; // assegna il valore ricavato dallo scanning

}
key_state = STATE_START_SCAN; // abilita lo scanning
} else if (key_out.state == KEY_PRESSED) { // ora il tasto è premuto, si ma nota l’else
if (key_out.key == internal_old_state_key_code) { // questa if viene valutata al prossimo turno
key_out.time++; // e internal_old… contiene un valore aggiornato
key_state = STATE_START_SCAN; // che potrebbe essere diverso a quello precendente
// salvato nella struct key_out.key
// lo scanning è abilitato e il key_out.time viene incrementato

} else if (internal_old_state_key_code == 0) { // controlla al prossimo turno dopo un nuovo scanning
se i tasti sono stati rilasciati, in tal caso ferma lo scanning
e metto lo stato tasto pronto che impedisce la valutazione
fintantoché il tasto non viene letto e usato da altro codice

key_state = STATE_END_SCAN;
key_out.state = KEY_READY;

} else { // altrimenti se non è uguale, lo stato è rilasciato e lo scanning è abilitato
key_out.state = KEY_RELEASED;
key_state = STATE_START_SCAN;

}
}
}

Sembra funzionare bene, mi da il tempo di premere i due tasti contemporaneamente con una certa tolleranza
e mi ritorna il tempo di permanenza pressed in millesimi di secondo.

Per leggere il/i tasti premuti uso questa funzione:

struct key_buffer *get_key()
{
    if (key_out.state == KEY_READY) {
        key_out_ready = key_out;
        key_out_ready.time *= 109;
        key_out.state = KEY_RELEASED;
        key_out.time = 0;
        key_state = STATE_START_SCAN;
        return &key_out_ready;
    }
    else
        return 0;
}

Che mi ritorna un puntatore ad una copia di struttura, oppure un puntatore nullo (0) se non ci sono tasti premuti.

Il codice è da pulire e devo trovare dei nomi di variable più adatti, ma questo domani sera se rimane tempo, intanto se hai consigli, spara pure.

PS: il codice mi è costato due giorni e parecchi mal di testa, la cosa è strana perchè alla fine il codice
non è per niente di complesso.

Ciao.