Timer con attiny85

Seguendo i consigli di Leo72 in questo topic:
https://forum.arduino.cc/index.php?topic=128284.15
ho voluto costruire un timer che scattasse ogni microsecondo, in modo poi che riesco a far muovere un uscita ed avere un'onda quadra che posso controllare in frequenza e ampiezza.
Forse ci sono soluzioni migliori della mia, ma come primo inizio ho voluto sperimentare quanto segue:

boolean up_dw = true;    //mi dice se sale o scende
byte on, off = 0;
byte ton = 4;
byte toff = 5;

void setup() {
  pinMode(4, OUTPUT);
  cli();//fermo gli interrupt
  TCCR0A &= ~((1 << COM0A0) | (1 << COM0A1)); //disattivi gli interrupt del timer
  //modalità contatore fino all'overflow
  TCCR0A &= ~((1 << WGM01) | (1 << WGM00));
  TCCR0B &= ~(1 << WGM02);
  //prescaler a /8
  TCCR0B &= ~((1 << CS02) | (1 << CS00));
  TCCR0B |= (1 << CS01);
  TCNT0 = 254; //valore iniziale
  TIMSK |= (1 << TOIE0); //attivo un interrutp all'overflow
  sei(); //riattivo gli interrupt
  //digitalWrite(4,HIGH);
}

void loop() {
  if (up_dw == true) {
    digitalWrite(4, HIGH);
  }
  else {
    digitalWrite(4, LOW);
  }

}

ISR (TIM0_OVF_vect) {
  if (up_dw == true) {
    on++;
  }
  if (up_dw == false) {
    off++;
  }
  if (on == ton && up_dw == true) {
    up_dw = false;
    on = 0;

Se non ho fatto male i conti dovrei trovarmi in uscita sul pin 4 un onda quadra con frequenza di circa 110Khz con un Dc di circa 44%....
Non mi interessa che sia preciso...
Il calcolo che ho fatto è stato:
freq di overflow=1microSec=1/0,000001= 1MHz
a questo punto calcolo il registro:
(8000000*0,000001)/prescaler/8=1
256-1=254

Il problema è che quando provo di colmpilare mi da errore:
Arduino:1.8.9 (Windows 10), Scheda:"ATtiny25/45/85, Enabled, CPU, ATtiny85, 8 MHz (internal), EEPROM retained, B.O.D. Disabled, Enabled"

wiring.c.o (symbol from plugin): In function `__vector_5':

(.text+0x0): multiple definition of `__vector_5'

C:\Users\MAURIZ~1.SOM\AppData\Local\Temp\arduino_build_23884\sketch\Multifrequeza.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

collect2.exe: error: ld returned 1 exit status

exit status 1
Errore durante la compilazione per la scheda ATtiny25/45/85.

Dove sbaglio??????

A parte questo, per ottenere la massima velocità usa PORT al posto di digitalWrite.

Mi sembra che si possa fare uscire il clock direttamente, senza dover scrivere su un'uscita...

Sì, ma solo con un oscillatore esterno, perché usa come uscita il pin XTAL2:

6.4 Clock Output Buffer
The device can output the system clock on the CLKO pin (when not used as XTAL2 pin). To enable the output, the CKOUT Fuse has to be programmed. This mode is suitable when the chip clock is used to drive other circuits on the system. Note that the clock will not be output during reset and that the normal operation of the I/O pin will be overridden when the fuse is programmed. Internal RC Oscillator, WDT Oscillator, PLL, and external clock (CLKI) can be selected when the clock is output on CLKO. Crystal oscillators (XTAL1, XTAL2) can not be used for clock output on CLKO. If the System Clock Prescaler is used, it is the divided system clock that is output.

Perdonami Datman, ma nno ti seguo...
Puoi chiarirmi??
Perchè il compilatore mi da errore???

Devi chiudere due graffe
Questo non e bello:
byte on, off = 0;

Sembrerebbe che il problema sia nel vettore dell'interrupt :o , e cioè
ISR (TIM0_OVF_vect) {... da errore
ISR (TIM0_OVFvect) {... non da errore

Domani provo con l'oscilloscopio per vedere se funziona, poi vi aggiorno...

Qualche altro consiglio su come fare?

Datman, come sostituisco l'istrunione digitalwrite con PORT come da te suggerito??

Datman:
Devi chiudere due graffe
Questo non e bello:
byte on, off = 0;

Non è bello o non è funzionale?
Cosa c'è che non va?

È preferibile scrivere:
byte on=0;
byte off=0;

Comunque, nella funzione di interrupt che hai pubblicato mancano due graffe alla fine. Non so se non le hai copiate da lì a qui...

Qui i nomi dei vettori di interrupt.

Per il PORT, cerca port manipulation. È molto semplice, è solo un'istruzione, ma bisogna indirizzare direttamente la porta.

Esempi:

PORTB |= 0b00000001;
porta a 1 il bit 0 della porta B, senza toccare gli altri bit.

PORTB &= 0b11111110;
porta a 0 lo stesso bit, senza toccare gli altri bit.

PORTD fa lo stesso sulla porta D.

Datman:
È preferibile scrivere:
byte on=0;
byte off=0;

Comunque, nella funzione di interrupt che hai pubblicato mancano due graffe alla fine. Non so se non le hai copiate da lì a qui...

Si è vero mi sono dimenticato due cose, le graffer e ricaricate il timer...
Ecco la routine

ISR (TIM0_OVFvect) {
  if (up_dw == true) {
    on++;
  }
  if (up_dw == false) {
    off++;
  }
  if (on == ton && up_dw == true) {
    up_dw = false;
    on = 0;
  }
  if (off == toff && up_dw == false) {
    up_dw = true;
    off = 0;
  }

  TCNT0 = 254;          //Ricarico il registro
}

Ho modificato anche il loop

void loop() {
  if (up_dw == true) {
  
    PORTB |=0b00010000;  //  digitalWrite(4, HIGH);
  }
  else {

    PORTB &=0b11101111;    //  digitalWrite(4, LOW);
  }

}

Solo che non va..... :cold_sweat:

Quindi, tutto viene così:

boolean up_dw = true;    //mi dice se sale o scende
byte on = 0;
byte off = 0;
byte ton = 4;
byte toff = 5;

void setup()
  {
  pinMode(4, OUTPUT);
  cli();//fermo gli interrupt
  TCCR0A &= ~((1 << COM0A0) | (1 << COM0A1)); //disattivi gli interrupt del timer
  //modalità contatore fino all'overflow
  TCCR0A &= ~((1 << WGM01) | (1 << WGM00));
  TCCR0B &= ~(1 << WGM02);
  //prescaler a /8
  TCCR0B &= ~((1 << CS02) | (1 << CS00));
  TCCR0B |= (1 << CS01);
  TCNT0 = 254; //valore iniziale
  TIMSK |= (1 << TOIE0); //attivo un interrutp all'overflow
  sei(); //riattivo gli interrupt
  }

void loop()
  {
  if (up_dw == true) {PORTD |=0b00010000;}
           // oppure: PORTD |= 1<<4; 
  else {PORTD &=0b11101111;}
  // o: PORTD &= ~(1<<4); 
  }


ISR (TIM0_OVFvect)
  {
  if (up_dw == true) {on++;}
  if (up_dw == false) {off++;}
  if (on == ton && up_dw == true)
    {
    up_dw = false;
    on = 0;
    }
  if (off == toff && up_dw == false)
    {
    up_dw = true;
    off = 0;
    }
  TCNT0 = 254;          //Ricarico il registro
  }
boolean up_dw = true;    //mi dice se sale o scende
byte on, off = 0;
byte ton = 4;
byte toff = 5;

void setup() {
  pinMode(4, OUTPUT);
  cli();                                      //fermo gli interrupt
  TCCR0A &= ~((1 << COM0A0) | (1 << COM0A1)); //disattivi gli interrupt del timer
  //modalità contatore fino all'overflow
  TCCR0A &= ~((1 << WGM01) | (1 << WGM00));
  TCCR0B &= ~(1 << WGM02);
  //prescaler a /8
  TCCR0B &= ~((1 << CS02) | (1 << CS00));
  TCCR0B |= (1 << CS01);
  TCNT0 = 254; //valore iniziale
  TIMSK |= (1 << TOIE0); //attivo un interrutp all'overflow
  sei(); //riattivo gli interrupt
  //digitalWrite(4,HIGH);
}

void loop() {
  if (up_dw == true) {
  
    PORTB |=0b00010000;  //  digitalWrite(4, HIGH);
  }
  else {
  
    PORTB &=0b11101111;  //  digitalWrite(4, LOW);
  }

}

ISR (TIM0_OVFvect) {
  if (up_dw == true) {
    on++;
  }
  if (up_dw == false) {
    off++;
  }
  if (on == ton && up_dw == true) {
    up_dw = false;
    on = 0;
  }
  if (off == toff && up_dw == false) {
    up_dw = true;
    off = 0;
  }

  TCNT0 = 254;          //Ricarico il registro
}

Questo è il tutto compila ma non va

L'I/O 4 è sulla porta D! Quindi devi scrivere PORTD |=...

Datman, l'Attiny85 ha solo la PORT B, infatti se provi a compilare con PORT D da errore....
Qualcuno che può aiutarmi?
Grazie.

Uh! E' l'85! Perdonami.

Sono demoralizzato...
Leggendo anche il datasheet e altre info sui timer mi sembra di aver fatto tutto giusto...
Ma non funziona nulla.....
Maurotec, ho visto il link ma se metto TIM0_OVF_vect il compilatore mi da errore, mentre se metto TIM0_OVFvect compila senza problemi.

Cosa sbaglio? MI potete aiutare per cortesia?
Grazie.

manolomao:
... ma se metto TIM0_OVF_vect il compilatore mi da errore, mentre se metto TIM0_OVFvect ...

Premetto che NON ho letto tutta la discussione, ma solo il problema e la domanda del post finale ...

La sintassi esatta è TIM0_OVF_vect ed è anche facile verificarlo ... da "iotnx5.h", che è il file delle definizioni per la serie ATtiny85:

...
/* Timer/Counter0 Overflow */
#define TIM0_OVF_vect_num		5
#define TIM0_OVF_vect			_VECTOR(5)
#define TIMER0_OVF_vect_num		5
#define TIMER0_OVF_vect			_VECTOR(5)
#define SIG_OVERFLOW0			_VECTOR(5)
...

in cui si vede che tale vettore è mappato esattamente su _VECTOR(5).

Ovvio che se metti un qualche cosa di inesistente, NON veine associato nessun vvector e quindi NON hai il conflitto con _VECTOR(5) ... ma è altrettanto ovvio che NON funziona :slight_smile:

Il mio sospetto è che ... Timer0, che tu stai tentando di usare, è il timer usato dal "core" di Arduino che già definisce tale ISR (come pensi possano funzionare millis() e micros()?) quindi ... tu non puoi ridefinirla perché, ovviamente, il compilatore ti dice che _VECTOR(5) è già usato.

Scegli un'altro timer ... es. Timer1 le cui definizioni della ISR che ti interessa puntano a _VECTOR(4):

...
/* Timer/Counter1 Overflow */
#define TIM1_OVF_vect_num		4
#define TIM1_OVF_vect			_VECTOR(4)
#define TIMER1_OVF_vect_num		4
#define TIMER1_OVF_vect			_VECTOR(4)
#define SIG_OVERFLOW1			_VECTOR(4)
...

Guglielmo

Grazie Guglielmo, mi ero perso la questione dei millis()....
Ora ho modificato e ho configurato il TIMER 1

boolean up_dw = true;    //mi dice se sale o scende
byte on, off = 0;
byte ton = 4;
byte toff = 5;

void setup() {
  pinMode(4, OUTPUT);
  cli();                                      //fermo gli interrupt
  TCCR1 &= ~((1 << COM1A0) | (1 << COM1A1)); //disattivi gli interrupt del timer
  //modalità contatore fino all'overflow
  TCCR1 &= ~((1 << WGM01) | (1 << WGM00));

  //prescaler a /8
  TCCR1 &= ~((1 << CS02) | (1 << CS00));
  TCCR1 |= (1 << CS01);
  TCNT1 = 254; //valore iniziale
  TIMSK |= (1 << TOIE1); //attivo un interrutp all'overflow
  sei(); //riattivo gli interrupt

}

void loop() {
  if (up_dw == true) {
    PORTB |=0b00010000;  //porta a 1 l'uscita pin 4 (pin 3 fisico)
  }
  else {
    PORTB &=0b11101111;   //porta a 0 l'uscita pin 4 (pin 3 fisico)
  }

}

ISR (TIM1_OVF_vect){
  if (up_dw == true) {
    on++;
  }
  if (up_dw == false) {
    off++;
  }
  if (on == ton && up_dw == true) {
    up_dw = false;
    on = 0;
  }
  if (off == toff && up_dw == false) {
    up_dw = true;
    off = 0;
  }

  TCNT1 = 254;          //Ricarico il registro
}

Però in uscito mi trovo 38microSec per il Ton e 35 microSec per il Toff....
:frowning:
Dove sbaglio ancora??
Altra domanda: se volessi usare il TIMER0 ed ignorare la funzione millis(), posso togliere l'enable dal menu strumenti???