Lettura / scrittura digitale con registri

Ciao raga, buon natale in anticipo a tutti :grin:!
Volevo porvi un quesito natalizio. Sto scrivendo delle funzioni che mi permettano di leggere o modificare lo stato di un pin in tempi piu' brevi di quelli raggiungibili con digitalWrite/Read. Questo dovrebbe voler dire :roll_eyes: setta come output la porta analogica 0 e rendila HIGH

DDRC = B00000001;
PORTC = B00000001;

Ora avrei bisogno di poter salvare o 0 o 1 in una variabile in base alla lettura di questa porta:

DDRC = B00000000; // setta analog 0 come input (in realtà  tutte come input :))
PORTC = B00000000; // disattiva la pull up
int BITreading = PINC & (1<<PC0); // leggi la porta 0

Teoricamente sembra corretto come è scritto ma non funziona, mi da valori abbastanza casuali...

Io ho provato con i PIC se ricordi la discussione su gioblu....
Però non usavo le variabili ma confrontavo direttamente registro per registro :slight_smile:

E se dichiari la variabile come byte?
Poi, mi spieghi perché questa linea di codice?

int BITreading = PINC & (1<<PC0); // leggi la porta 0

Questo è un AND sui bit fra PINC ed 1 spostato verso sinistra del valore di PC0. Perché? (:

Ciao ragazzi. Grazie delle risposte... allora.
Sto lavorando a un protocollo di comunicazione custom ormai da mesi, che si basa su un singolo pin per comunicazione bidirezionale.
Per questo ho bisogno di tempi veloci di setting del PIN per poter raggiungere il goal di 10kb/s.
Attualmente con l'uso di digitalWrite / Read sono a 500 bytes/s

Allora:

    int BITreading = PINC & (1<<B00000000);

Con questa linea di codice la ricezione funziona incredibilmente con i registri :grin:, ottengo 0 se la porta analog 0 è LOW 1 se è HIGH.
Quindi siamo a metà dell'opera.

Adesso devo aggiornare il tx partendo da questa funzione:

  void writeBIT(int VALUE, int duration) {
    pinmode(inputPIN, OUTPUT);
    digitalWrite(inputPIN, VALUE);
    delayMicroseconds(duration - digitalRWTime); //for BITwidth 
  }

E sostituirla con una che usi i registri, esempio, una mia prova non funzionante:

  void writeBIT(int VALUE, int duration) {
    DDRC = B00000001;
    if(VALUE == 1) { PORTC = B00000001; } else { PORTC = B00000000; }
    delayMicroseconds(duration - digitalRWTime);
  }

Ci sto sbattendo la testa da un paio di giorni e per fortuna ora quantomeno la ricezione funziona, stavo iniziando a pensare di desistere :grin:

NeXTWay:
E se dichiari la variabile come byte?
Poi, mi spieghi perché questa linea di codice?

int BITreading = PINC & (1<<PC0); // leggi la porta 0

Questo è un AND sui bit fra PINC ed 1 spostato verso sinistra del valore di PC0. Perché? (:

No, nella manipolazione dei singoli bit, questa operazione equivale a mettere ad 1 il pin indicato da PC0 e poi a farne l'AND con il contenuto di PINC.

@gbm:
C'è però un errore di fondo. Non si deve dichiarare BITreading di tipo "int": gbm, stai manipolando 1 byte, non un int :stuck_out_tongue:
Quindi usa "byte".

Anche qui non va bene:

void writeBIT(int VALUE, int duration) {
    DDRC = B00000001;
    if(VALUE == 1) { PORTC = B00000001; } else { PORTC = B00000000; }
    delayMicroseconds(duration - digitalRWTime);
}

VALUE è meglio che sia di tipo byte. Poi le manipolazioni sui bit non si fanno così, in questo modo non tieni conto dello stato degli altri pin alterando tutto il contenuto della porta, anche se magari gli altri bit non devono essere interessati all'operazione. Intanto potresti fare così:

void writeBIT(byte VALUE, int duration) {
    DDRC |= B00000001;
    if(VALUE == 1) { PORTC |= B00000001; } else { PORTC &= B00000000; }
    delayMicroseconds(duration - digitalRWTime);
  }

Ciao Leo, grazie dei consigli. Hai ragione in effetti byte è doveroso heheheh...
Prima di leggere il tuo post sono riuscito a far funzionare la ricezione, usando questa linea:

int BITreading = PINC & (1<<B00000000);

Sarebbe carino capire perchè funziona con int :grin:

Ho provato a utilizzare la funzione TX che mi hai consigliato ma all'RX arriva solo ciarpame (come al solito, in tutte le prove di oggi in cui provo a usare registri in tx).
Con la vecchia versione TX a digitalWrite arriva corretto, anche utilizzando int BITreading = PINC & (1<<B00000000);. Non riesco a capire come sia possibile!!

In qualsiasi caso utilizzando i registri in RX sono riuscito a scendere a 150micros x bit, che sono + o - 579 bytes/s
Ci deve essere qualche errore di fondo, tipo che serve una pulldown o qualcosa del genere, non ho idea...non me lo so spiegare.

Le pull-down sui GPIO dell'Atmega non esistono, esistono solo pull-up.
Cmq leggendo il datasheet sembrerebbe che prima andasse impostato il segnale sul pin (quindi manipolato PORTC) e poi data la direzione del pin (DDRC). Sempre sul datasheet viene riportato che tra l'impostazione del segnale e quella della direzione del pin, viene attivata temporaneamente la pull-up interna. Non so se tale cosa può esser letta come segnale nel tuo sistema di trasmissione.

Questo è il codice di digitalWrite:

void digitalWrite(uint8_t pin, uint8_t val)
{
	uint8_t timer = digitalPinToTimer(pin);
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	volatile uint8_t *out;

	if (port == NOT_A_PIN) return;

	// If the pin that support PWM output, we need to turn it off
	// before doing a digital write.
	if (timer != NOT_ON_TIMER) turnOffPWM(timer);

	out = portOutputRegister(port);

	if (val == LOW) {
		uint8_t oldSREG = SREG;
                cli();
		*out &= ~bit;
		SREG = oldSREG;
	} else {
		uint8_t oldSREG = SREG;
                cli();
		*out |= bit;
		SREG = oldSREG;
	}
}

Come vedi, fa ciò che ti ho detto io: in caso di segnale LOW, spenge il pin relativo alla porta con l'AND su 0, lo accende con l'OR su 1 in caso di segnale HIGH.

sorgono spontanee alcune domante.. fa una cli() ma non chiama la sei()...

cosìè SREG?

Sempre sul datasheet viene riportato che tra l'impostazione del segnale e quella della direzione del pin, viene attivata temporaneamente la pull-up interna

questo è un bel problema quando si switcha da TX a RX e viceversa, ma per ora non è un problema

Ciao ragazzi, ho provato tutte le combinazioni possibili... niente.
Ho provato in un codice di prova ad accendere e spegnere il led 13 con i registri e funziona... ma se li uso nel mio codice qualcosa non va...
Non riesco a capire come possa ottenere due risultati diversi con due funzioni identiche, quando poi in ricezione funziona perfettamente anche con i registri...

Il modo più semplice, e veloce, per controllare i singoli bit di un registro è usare la SBI e CBI assembly, puoi farlo da C usando l'assembly inline oppure le definisci come macro define in questo modo:

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

Poi da C le usi in questo modo:

sbi(PORTB,5); // setta il bit 5 PORTB (led acceso)

cbi(PORTB,5); // resetta il bit 5 PORTB (led spento)

Ovviamente devi prima settare come out l'eventuale pin che vuoi controllare, verifica sullo schema la corrispondenza tra pin di Arduino e pin/port reali del AVR.
Le macro vengono compilate utilizzando una singola linea assembly, cioè la SBI o la CBI che richiedono due cicli macchina (125 ns @ 16 MHz) per l'esecuzione e, ovviamente, non hanno alcun effetto sugli altri bit del registro.
CBI e SBI si possono usare su tutti i registri del AVR, quindi non solo per i PORTx, vanno benissimo anche per settare i vari bit dei registri macchina per le varie periferiche e funzioni di sistema.

p.s.
Buttate il C++ alle ortiche e imparate ad usare il C vero, cioè quello descritto sul K&R poi standardizzato come C ANSI :grin:

Bella soluzione, Astro!

Ciao raga, buon Natale!
Non funzionaaa :fearful: :fearful: :fearful: :fearful:
Non riesco davvero a capire come mai!!

Con digitalWrite funziona utilizzando qualsiasi tipo di registro in qualsiasi salsa no.
La analog 0 è la PORC ultimo BIT a destra, ne son sicuro, ma cmq non funziona.

gbm:
Non riesco davvero a capire come mai!!

Perché non leggi correttamente lo schema, Analag 0 è PORTC 0, cioè il primo bit :smiley:
SBI e CBI funzionano perfettamente, le uso normalmente in quasi tutti i miei programmi per AVR.

Sei sicuro? Quindi usando PORTC dovrei scrivere: PORTC = B10000000; ?
Perchè provo a far lampeggiare il PIN 13 con A0 e la riga: PORTC = B00000001; un delay e PORTC = B00000000; e ottengo risultato positivo
Pero' se la integro nel mio protocollo non funziona, non capisco cosa possa cambiare...

gbm:
Sei sicuro? Quindi usando PORTC dovrei scrivere: PORTC = B10000000; ?

Per settare Analog 0 usa sbi (PORTC,0), per resettarlo cbi (PORTC,0), ricordati che prima devi impostare A0 come out digitale altrimenti non puoi scriverci sopra.

Si sto usando proprio: if(VALUE == HIGH) { sbi(PORTC,0); } else { cbi(PORTC,0); }
Facendo i test con la macro che hai consigliato, ma ottengo lo stesso risultato di PORTC

A questo punto forse i tempi di attuazione sfasano tutto, non c'è altra spiegazione...
Pero' mi sembra strano perchè uso:

void writeBIT(byte VALUE, int duration) {
    long time = micros();
    if(VALUE == HIGH) { sbi(PORTC,0); } else { cbi(PORTC,0); }
    delayMicroseconds(duration - (micros() - time)); //for BITwidth 
  }

In una funzione che richiama questa metto DDRC = B00000001;
ma niente, se sostituisco la if con:

digitalWrite(inputPIN, VALUE);

funziona =( =( =( =( =(

L'unica cosa che mi tira su il morale è che sono quasi a 1kb/s con 100 micros di durata di un bit

gbm:
if(VALUE == HIGH) { sbi(PORTC,0); } else { cbi(PORTC,0); }

Sintassi ambigua che porta facilmente ad interpretazioni errate del compilatore anche se non vengono segnalati errori, sicuramente ci sono delle warning che non vedi senza attivare la modalità verbose.
Scrivila così:

if(VALUE == HIGH)  sbi(PORTC,0);
else  cbi(PORTC,0);

Bah, inizio a pensare di desistere e tenermi 1 kb/s...
ricevo solo caratteri errati che vengono scartati e "@"

gbm:
Bah, inizio a pensare di desistere e tenermi 1 kb/s...
ricevo solo caratteri errati che vengono scartati e "@"

Ma non è che il problema è dal lato opposto, cioè in ricezione, che magari fai con digitalread che è lenta come la digitalwrite, e con la commutazione veloce del pin non riesci a leggere in tempo lo stato logico.

no, usa i registri anche in lettura... però visto che la srcrittura viene rallentata con delaymicros(), in teoria settando il guisto valore non dovrebbero esserci problemi..