[Résolu] Attiny85 - pb timer & I2c slave

Bonjour tout le monde !

J'ai un (enfin deux) probléme avec un projet en cours, et j'arrive pas à m'en sortir :~

Je suis en train de faire un systéme à base d'ATtiny85 qui génère deux signaux carré de fréquence défini.
Pour ceux faire j'utilise timer0 et timer1, le schéma de mon montage est en pièce jointe.

Le principe : L'ATtiny attend sur le bus I2C deux octets, un premier qui sert de commande, et un deuxième qui sert de valeur.

En gros les "paquets" I2C on cette forme (syntaxe : bus pirate) :
[ adresseI2C canal fréquence ]

"canal" peut prendre les valeurs 0, 1 ou 2.
0 étant un canal spécial qui permet d'arrêter un timer.

Les fréquences sont codé via une valeur de 0 à 127, qui correspond aux notes de musique que génère miditones à partir d'un fichier midi (Google Code Archive - Long-term storage for Google Code Project Hosting.).

Exemple :
[ 0 1 69 ] -> 440Hz sur ch1
[ 0 2 69 ] -> 440Hz sur ch2
[ 0 0 1 ] -> ch1 silence
[ 0 0 2 ] -> ch2 silence

Sauf que (se serait trop beau si tout marchait du 1er coup)

Dans mon code je défini 0x20 comme adresse d'esclave :

/* I2C Node Address */
#define I2C_NODE_ADDRESS 0x20

Puis j'utilise la librairie usiTwiSlave pour gérer les communications I2C

/* Seting up I2C Slave */
usiTwiSlaveInit(I2C_NODE_ADDRESS);
sei();

Je devrai donc pouvoir communiquer avec mon Attiny sur l'adresse 0x20, sauf que aprés avoir fait un scan rapide avec ma carte bus pirate, il s'avére que mon Attiny répond sans probléme, mais sur l'adresse 0x00 !
Comme je prévoie de faire 8 modules il faut absolument que la gestion des adresses marche :~

Ensuite probléme plus grave, j'utilise les timer 0 & 1 en CTC (clear on timer compare) pour générer les deux signaux.
Sauf que j'ai une énorme différence de fréquence entre timer1 et timer0, pourtant je pense avoir correctement choisi mes prescaller ...

Exemple : [ 0 1 69 ] -> 440Hz sur ch1, en sortie sur mon fréquencemètre : 439Hz (Ok)
[ 0 2 69 ] -> 440Hz sur ch2, en sortie sur mon fréquencemètre : 121.5Hz (!?)
ch1 semble donc fonctionner, mais ch2 pas pas du tout, en faite je crois que mes prescaller pour le timer2 sont complétement faux :confused:

Bref ... mon truc marche pas et je vois pas comment m'en sortir =(
Help ... Doit bien y avoir un mordu de datasheet et de registre dans l'assistance :grin:

Mon code :

/*
 * TinySound - Simple, I2C controled, 2 channel music generator
 * Coded by SkyWodd <skyduino.wordpress.com> 
 *
 * Based on arduino-playtune (http://code.google.com/p/arduino-playtune/)
 *      and miditones (http://code.google.com/p/miditones/)
 */

/*
 * ATtiny85 Fuses : 
 * PLL Clock; Start-up time PWRDWN/RESET: 1K CK/14 CK + 4 ms; [CKSEL=0001 SUT=00]
 * Brown-out detection level at VCC=4.3 V; [BODLEVEL=100]
 * Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]
 * Serial program downloading (SPI) enabled; [SPIEN=0]
 */
 
 /* Includes */
#include <avr/interrupt.h> /* For Timer Interrupt */
#include <avr/pgmspace.h>  /* For PROGMEM */
#include "usiTwiSlave.h"   /* For I2C Slave */
#include <avr/io.h>        /* For I/O usage */

/* I2C Node Address */
#define I2C_NODE_ADDRESS 0x20

 /* PinMapping Definition */
#define SOUND_OUT_1_BIT 3
#define SOUND_OUT_2_BIT 4
#define WORKING_LED_BIT 1 

/* Usage Macro */
#define setLedOn() PORTB |= (1 << WORKING_LED_BIT)
#define setLedOff() PORTB &= ~(1 << WORKING_LED_BIT)
#define setLedToggle() PORTB ^= (1 << WORKING_LED_BIT)

#define setChan1On() TIMSK |= (1 << OCIE0A)
#define setChan1Off() TIMSK &= ~(1 << OCIE0A); PORTB &= ~(1 << SOUND_OUT_1_BIT)

#define setChan2On() TIMSK |= (1 << OCIE1A)
#define setChan2Off() TIMSK &= ~(1 << OCIE1A); PORTB &= ~(1 << SOUND_OUT_2_BIT)

/* Chromatic Frequency Table */
const uint16_t PROGMEM tune_freq[128] =
{
    16,17,18,19,21,22,23,24,26,28,29,31,33,35,37,39,41,
    44,46,49,52,55,58,62,65,69,73,78,82,87,92,98,104,110,
    117,123,131,139,147,156,165,175,185,196,208,220,233,
    247,262,277,294,311,330,349,370,392,415,440,466,494,
    523,554,587,622,659,698,740,784,831,880,932,988,1047,
    1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976,
    2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729,
    3951,4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,
    7459,7902,8372,8870,9397,9956,10548,11175,11840,12544,
    13290,14080,14917,15804,16744,17740,18795,19912,21096,
    22351,23680,25088
};

/* Timer0 Interrupt */
ISR(TIMER0_COMPA_vect) { 
    PORTB ^= 1 << SOUND_OUT_1_BIT;
}

/* Timer1 Interrupt */
ISR(TIMER1_COMPA_vect) { 
    PORTB ^= 1 << SOUND_OUT_2_BIT;
}

/* Tune Channel to frequency */
void set_channel(uint8_t channel, uint8_t note) {
	uint8_t prescalarbits;
	uint16_t ocr, freq2 = pgm_read_word(tune_freq + note);
	
	// make sure the note isn't too low to be playable
	if (note < 24) return;  // ignore if so

    ocr = F_CPU / freq2 - 1;
    prescalarbits = 0b001;  // ck/1
    if (ocr > 255) {
	
		ocr = F_CPU / freq2 / 8 - 1;
		prescalarbits = (channel == 1) ? 0b010 : 0b100;  // ck/8
		if (ocr > 255) {
		
			ocr = F_CPU / freq2 / 64 - 1;
			prescalarbits = (channel == 1) ? 0b011 : 0b111; // ck/64
			if (ocr > 255) {
			
				ocr = F_CPU / freq2 / 256 - 1;
				prescalarbits = (channel == 1) ? 0b100 : 0b1001; // ck/256
				if (ocr > 255) {
				
					ocr = F_CPU / freq2 / 1024 - 1;
					prescalarbits = (channel == 1) ? 0b101 : 0b111; // ck/1024
				}
			}
		}
	}

	if (channel == 1) {
		TCCR0B = (TCCR0B & 0b11111000) | prescalarbits;
		OCR0A = ocr & 255;
	} else {
		TCCR1 = (TCCR1 & 0b11110000) | prescalarbits;
		OCR1A = ocr & 255;
	}
}

/* Entry Point */
int main(void) {
	/* Variables */
	uint8_t channel;
	uint8_t note;
	
	/* Channels & Led Pin As Output */
    DDRB |= (1 << SOUND_OUT_1_BIT) | (1 << SOUND_OUT_2_BIT) | (1 << WORKING_LED_BIT);

	/* Seting up Timer To CTC Mode */
	/* Timer0 */
	TCCR0A = 1 << WGM01;
	TCCR0B = 1 << CS00;

	/* Timer1 */
	TCCR1 = (1 << CS10) | (1 << CTC1);
	
	/* Stop Interrupt */
	setChan1Off();
	setChan2Off();
	setLedOff();
	
	/* Seting up I2C Slave */
	usiTwiSlaveInit(I2C_NODE_ADDRESS);
	sei();
	
	/* Forever loop */
	for(;;) {
		/* If Data Sent Over I2C */
		if(usiTwiDataInReceiveBuffer()) {
			setLedToggle();
			channel = usiTwiReceiveByte();
			note = usiTwiReceiveByte();
			switch(channel) {
				case 0:
					if(note == 1) {
						setChan1Off();
					} else if(note == 2) {
						setChan2Off();
					}
					break;
					
				case 1:
					set_channel(1, note & 127);
					setChan1On();
					break;
					
				case 2:
					set_channel(2, note & 127);
					setChan2On();
					break;
			}
		}
	}
}

Mon makefile :

DEVICE=attiny85
AVRDUDE = avrdude -c usbtiny -p $(DEVICE)

COMPILE = avr-gcc -Wall -Os -I. -mmcu=$(DEVICE) -DF_CPU=16000000 -DDEBUG_LEVEL=0
SIZE = avr-size --mcu=$(DEVICE)
OBJECTS = usiTwiSlave.c tinysound.c

all: $(OBJECTS) clean
	$(COMPILE) -o main.bin $(OBJECTS)
	avr-objcopy -j .text -j .data -O ihex main.bin main.hex
	$(SIZE) main.hex
	rm -f *.bin

flash:
	$(AVRDUDE) -U flash:w:main.hex

fuse:
	$(AVRDUDE) -U lfuse:w:0xc1:m -U hfuse:w:0xd4:m -U efuse:w:0xff:m

clean:
	rm -f *.o *.bin *.hex

salut skywodd !
pour le 'prescaler' du Timer 1 il s'agit peut être d'une simple faute de frappe :

ocr = F_CPU / freq2 / 1024 - 1;
prescalarbits = (channel == 1) ? 0b101 : 0b1011; // ck/1024

(Table 12-5 pages 92/93)

Ouaip effectivement y avait une petite faute de frappe.

Par contre j'ai 30Hz pour note=69 au lieu de 440Hz et pour note=100 j'ai 900Hz :astonished:
Ça passe d'un son trés grave à un son trés aigu sur une trés courte plage ... 90 -> 100Hz, 100 -> 900Hz ...

Je crois que mon probléme vient du fait que le timer1 utilise un signal d'horloge différent de timer0
D'aprés le datasheet timer0 utilise les 16MHz du cpu alors que timer1 utilise le signal en sortie de la PLL soit 64MHz (ou 32MHz j'ai pas trop compris !?).

Une petite idée de comment m'en sortir ?

le bit PCKE permet de sélectionner l'horloge envoyée eu prescaler du Timer 1, donc pas obligatoirement l'horloge PCK issue de la PLL.
(je n 'ai pas encore utilisé les Tiny45 ou 85 et ne connais pas toutes les fonctionalités d'horloges)
Le jour où je m'y mettrai je m'aiderai du 'Wizard' intégré à Codevision (version demo)
Pas mal pour dégrossir les configs de périphériques d'AVR !

Bon j'ai placé PCKE à 0 ce qui devrait faire que le timer1 tourne à 16MHz

Sauf que sur ch1 pour 69 j'ai bien 340Hz, mais sur ch2 ... 122Hz ... :stuck_out_tongue_closed_eyes:

Voile la derniére version de mon code :

/*
 * TinySound - Simple, I2C controled, 2 channel music generator
 * Coded by SkyWodd <skyduino.wordpress.com> 
 *
 * Based on arduino-playtune (http://code.google.com/p/arduino-playtune/)
 *      and miditones (http://code.google.com/p/miditones/)
 */

/*
 * ATtiny85 Fuses : 
 * PLL Clock; Start-up time PWRDWN/RESET: 1K CK/14 CK + 4 ms; [CKSEL=0001 SUT=00]
 * Brown-out detection level at VCC=4.3 V; [BODLEVEL=100]
 * Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]
 * Serial program downloading (SPI) enabled; [SPIEN=0]
 */
 
 /* Includes */
#include <avr/interrupt.h> /* For Timer Interrupt */
#include <avr/pgmspace.h>  /* For PROGMEM */
#include "usiTwiSlave.h"   /* For I2C Slave */
#include <avr/io.h>        /* For I/O usage */

/* I2C Node Address */
#define I2C_NODE_ADDRESS 0x20

 /* PinMapping Definition */
#define SOUND_OUT_1_BIT 3
#define SOUND_OUT_2_BIT 4
#define WORKING_LED_BIT 1 

/* Usage Macro */
#define setLedOn() PORTB |= (1 << WORKING_LED_BIT)
#define setLedOff() PORTB &= ~(1 << WORKING_LED_BIT)
#define setLedToggle() PORTB ^= (1 << WORKING_LED_BIT)

#define setChan1On() TIMSK |= (1 << OCIE0A)
#define setChan1Off() TIMSK &= ~(1 << OCIE0A); PORTB &= ~(1 << SOUND_OUT_1_BIT)

#define setChan2On() TIMSK |= (1 << OCIE1A)
#define setChan2Off() TIMSK &= ~(1 << OCIE1A); PORTB &= ~(1 << SOUND_OUT_2_BIT)

/* Chromatic Frequency Table */
const uint16_t PROGMEM tune_freq[128] =
{
    16,17,18,19,21,22,23,24,26,28,29,31,33,35,37,39,41,
    44,46,49,52,55,58,62,65,69,73,78,82,87,92,98,104,110,
    117,123,131,139,147,156,165,175,185,196,208,220,233,
    247,262,277,294,311,330,349,370,392,415,440,466,494,
    523,554,587,622,659,698,740,784,831,880,932,988,1047,
    1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976,
    2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729,
    3951,4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,
    7459,7902,8372,8870,9397,9956,10548,11175,11840,12544,
    13290,14080,14917,15804,16744,17740,18795,19912,21096,
    22351,23680,25088
};

/* Timer0 Interrupt */
ISR(TIMER0_COMPA_vect) { 
    PORTB ^= 1 << SOUND_OUT_1_BIT;
}

/* Timer1 Interrupt */
ISR(TIMER1_COMPA_vect) { 
    PORTB ^= 1 << SOUND_OUT_2_BIT;
}

/* Tune Channel to frequency */
void set_channel(uint8_t channel, uint8_t note) {
	uint8_t prescalarbits;
	uint16_t ocr, freq2 = pgm_read_word(tune_freq + note);
	
	// make sure the note isn't too low to be playable
	if (note < 12) return;  // ignore if so

    ocr = F_CPU / freq2 - 1;
    prescalarbits = 0b001; // ck/1
    if (ocr > 255) {
	
		ocr = F_CPU / freq2 / 8 - 1;
		prescalarbits = (channel == 1) ? 0b010 : 0b100; // ck/8
		if (ocr > 255) {
		
			ocr = F_CPU / freq2 / 64 - 1;
			prescalarbits = (channel == 1) ? 0b011 : 0b111; // ck/64
			if (ocr > 255) {
			
				ocr = F_CPU / freq2 / 256 - 1;
				prescalarbits = (channel == 1) ? 0b100 : 0b1001; // ck/256
				if (ocr > 255) {
				
					ocr = F_CPU / freq2 / 1024 - 1;
					prescalarbits = (channel == 1) ? 0b101 : 0b1101; // ck/1024
				}
			}
		}
	}

	if (channel == 1) {
		TCCR0B = (TCCR0B & 0b11111000) | prescalarbits;
		OCR0A = ocr & 255;
	} else {
		TCCR1 = (TCCR1 & 0b11110000) | prescalarbits;
		OCR1A = ocr & 255;
	}
}

/* Entry Point */
int main(void) {
	/* Variables */
	uint8_t channel;
	uint8_t note;
	
	/* Channels & Led Pin As Output */
    DDRB |= (1 << SOUND_OUT_1_BIT) | (1 << SOUND_OUT_2_BIT) | (1 << WORKING_LED_BIT);

	/* Seting up Timer To CTC Mode */
	/* Timer0 */
	TCCR0A = 1 << WGM01;
	TCCR0B = 1 << CS00;

	/* Timer1 */
	TCCR1 = (1 << CS10) | (1 << CTC1);
	PLLCSR &= ~(1 << PCKE);
	
	/* Stop Interrupt */
	setChan1Off();
	setChan2Off();
	setLedOff();
	
	/* Seting up I2C Slave */
	usiTwiSlaveInit(I2C_NODE_ADDRESS);
	sei();
	
	/* Forever loop */
	for(;;) {
		/* If Data Sent Over I2C */
		if(usiTwiDataInReceiveBuffer()) {
			setLedToggle();
			channel = usiTwiReceiveByte();
			note = usiTwiReceiveByte();
			switch(channel) {
				case 0:
					if(note == 1) {
						setChan1Off();
					} else if(note == 2) {
						setChan2Off();
					}
					break;
					
				case 1:
					set_channel(1, note & 127);
					setChan1On();
					break;
					
				case 2:
					set_channel(2, note & 127);
					setChan2On();
					break;
			}
		}
	}
}

Yep!

C'est peut être un peu bête comme question, mais pourquoi avoir mis CTC1 enable ???

@+

Zoroastre.

zoroastre:
C'est peut être un peu bête comme question, mais pourquoi avoir mis CTC1 enable ???

J'ai cherché dans tout le datasheet comment mettre le timer1 en mode CTC (clear on timer compare match) mais j'ai rien trouvé ...
Du coup dans la récap des registre j'ai vu CTC1 ... je me suis dis que ça devait être ça.

Je vais tenter sans mettre (1 << CTC1) peut être que ...

Edit: PROBLÈME 1/2 RÉSOLU

D'aprés le datasheet page 92, section 12.31.1

Bit 7 – CTC1 : Clear Timer/Counter on Compare Match
When the CTC1 control bit is set (one), Timer/Counter1 is reset to $00 in the CPU clock cycle
after a compare match with OCR1C register value. If the control bit is cleared, Timer/Counter1
continues counting and is unaffected by a compare match.

Mon code travaillait avec OCR1A, qui sert sert à faire de la PWM, je viens de faire le modification, tout fonctionne \o/

Probléme suivant : Pourquoi mon code répond sur l'adresse 0x00 et non 0x20 !?

As-tu essayé en ne passant pas par une variable en utilisant directement

/* Seting up I2C Slave */
usiTwiSlaveInit(0x20);
sei();

B@tto:
As-tu essayé en ne passant pas par une variable en utilisant directement

/* Seting up I2C Slave */

usiTwiSlaveInit(0x20);
sei();

Non, mais je viens de trouver une version beaucoup plus récente de usiTwiSlave qui justement comporte un bug-fix pour les attiny45 et 85.
En plus la nouvelle version utilise des callbacks pour les écritures/lectures de registres, c'est juste ce qu'il me faut !

-> Google Code Archive - Long-term storage for Google Code Project Hosting.

Au passage, en déclarant l'adresse I2C 0x20, il faut aller demander le périph I2C 0x40 pour l'écriture, heureusement que ma carte bus pirate fait la conversion automatiquement sinon j'aurai jamais réussi à faire quoi que ce soit sur 0x20 :sweat_smile:

Problème résolu, merci tout le monde !
Promis des que je peut je fait un article riche en son divers et varié sur le sujet :grin:

Yep!

D'aprés le datasheet page 92, section 12.31.1

Bit 7 – CTC1 : Clear Timer/Counter on Compare Match
When the CTC1 control bit is set (one), Timer/Counter1 is reset to $00 in the CPU clock cycle
after a compare match with OCR1C register value. If the control bit is cleared, Timer/Counter1
continues counting and is unaffected by a compare match.

Mon code travaillait avec OCR1A, qui sert sert à faire de la PWM, je viens de faire le modification, tout fonctionne \o/

J'avais vu cette partie également et c'est ce qui m'a mis la puce à l'oreille. Mais c'est clair que le datasheet du 85 est ardu (bien plus que le 2313 que je viens d'experimenter) !!!

Promis das que je peut je fait un article riche en son divers et varié sur le sujet

Super :wink:

@+

Zoroastre.