Pages: [1]   Go Down
Author Topic: [Résolu] Attiny85 - pb timer & I2c slave  (Read 1115 times)
0 Members and 1 Guest are viewing this topic.
France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5420
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonjour tout le monde !

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

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 (http://code.google.com/p/miditones/).

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 :
Code:
/* I2C Node Address */
#define I2C_NODE_ADDRESS 0x20

Puis j'utilise la librairie usiTwiSlave pour gérer les communications I2C
Code:
/* 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 smiley-confuse

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 :/

Bref ... mon truc marche pas et je vois pas comment m'en sortir smiley-cry
Help ... Doit bien y avoir un mordu de datasheet et de registre dans l'assistance smiley-mr-green

Mon code :
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 :
Code:
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


* image201203030001.jpg (63.93 KB, 744x685 - viewed 159 times.)
« Last Edit: March 05, 2012, 05:50:40 am by skywodd » Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

France
Offline Offline
God Member
*****
Karma: 4
Posts: 980
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

salut skywodd !
pour le 'prescaler' du Timer 1 il s'agit peut être d'une simple faute de frappe :
Quote
ocr = F_CPU / freq2 / 1024 - 1;
prescalarbits = (channel == 1) ? 0b101 : 0b1011; // ck/1024
(Table 12-5 pages 92/93)
« Last Edit: March 04, 2012, 12:45:50 pm by al1fch » Logged

France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5420
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 smiley-eek
Ç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 ?
Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

France
Offline Offline
God Member
*****
Karma: 4
Posts: 980
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 !


* choix horloge Timer1.jpg (36.77 KB, 696x303 - viewed 14 times.)
« Last Edit: March 04, 2012, 03:39:35 pm by al1fch » Logged

France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5420
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 ... smiley-yell

Voile la derniére version de mon code :

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;
}
}
}
}
Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

0
Offline Offline
God Member
*****
Karma: 2
Posts: 802
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yep!

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

@+

Zoroastre.
Logged

Veuillez indiquer [RESOLU] dans l'entête du titre en éditant votre premier message smiley-wink

France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5420
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Quote
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 !?
« Last Edit: March 05, 2012, 05:23:06 am by skywodd » Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

Ales
Online Online
Faraday Member
**
Karma: 39
Posts: 3841
Do or DIY
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

As-tu essayé en ne passant pas par une variable en utilisant directement
Code:
/* Seting up I2C Slave */
usiTwiSlaveInit(0x20);
sei();
Logged


France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5420
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

As-tu essayé en ne passant pas par une variable en utilisant directement
Code:
/* 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 !

-> http://code.google.com/p/usi-i2c-slave/source/browse/trunk/

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 smiley-sweat

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 smiley-mr-green
« Last Edit: March 05, 2012, 02:51:18 pm by skywodd » Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

0
Offline Offline
God Member
*****
Karma: 2
Posts: 802
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yep!

Quote
D'aprés le datasheet page 92, section 12.31.1
Quote
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) !!!

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

Super smiley-wink

@+

Zoroastre.
Logged

Veuillez indiquer [RESOLU] dans l'entête du titre en éditant votre premier message smiley-wink

Pages: [1]   Go Up
Jump to: