Forum: Mikrocontroller und Digitale Elektronik ATMega Code auf ATTiny portieren


von Thomas (Gast)


Lesenswert?

Guten Tag,
ich habe ein sehr schönes simples Projekt gefunden, dass sich 
hervorragend als kleines Geschenk umsetzen lässt. Ich würd es jedoch 
gerne auf dem ATTiny85 laufen lassen. Es handelt sich um den sogenannten 
Auduino. Kann mir jemand dabei helfen, den Code auf den ATTiny zu 
portieren?

Es müssen wohl nur die Interrupts gesetzt werden, aber ich hab da nicht 
so den Überblick. Der Code sieht wie folgt aus:
1
// Auduino, the Lo-Fi granular synthesiser
2
//
3
// by Peter Knight, Tinker.it http://tinker.it
4
//
5
// Help:      http://code.google.com/p/tinkerit/wiki/Auduino
6
// More help: http://groups.google.com/group/auduino
7
//
8
// Analog in 0: Grain 1 pitch
9
// Analog in 1: Grain 2 decay
10
// Analog in 2: Grain 1 decay
11
// Analog in 3: Grain 2 pitch
12
// Analog in 4: Grain repetition frequency
13
//
14
// Digital 3: Audio out (Digital 11 on ATmega8)
15
//
16
// Changelog:
17
// 19 Nov 2008: Added support for ATmega8 boards
18
// 21 Mar 2009: Added support for ATmega328 boards
19
// 7 Apr 2009: Fixed interrupt vector for ATmega328 boards
20
// 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega)
21
22
#include <avr/io.h>
23
#include <avr/interrupt.h>
24
25
uint16_t syncPhaseAcc;
26
uint16_t syncPhaseInc;
27
uint16_t grainPhaseAcc;
28
uint16_t grainPhaseInc;
29
uint16_t grainAmp;
30
uint8_t grainDecay;
31
uint16_t grain2PhaseAcc;
32
uint16_t grain2PhaseInc;
33
uint16_t grain2Amp;
34
uint8_t grain2Decay;
35
36
// Map Analogue channels
37
#define SYNC_CONTROL         (4)
38
#define GRAIN_FREQ_CONTROL   (0)
39
#define GRAIN_DECAY_CONTROL  (2)
40
#define GRAIN2_FREQ_CONTROL  (3)
41
#define GRAIN2_DECAY_CONTROL (1)
42
43
44
// Changing these will also requires rewriting audioOn()
45
46
#if defined(__AVR_ATmega8__)
47
//
48
// On old ATmega8 boards.
49
//    Output is on pin 11
50
//
51
#define LED_PIN       13
52
#define LED_PORT      PORTB
53
#define LED_BIT       5
54
#define PWM_PIN       11
55
#define PWM_VALUE     OCR2
56
#define PWM_INTERRUPT TIMER2_OVF_vect
57
#elif defined(__AVR_ATmega1280__)
58
//
59
// On the Arduino Mega
60
//    Output is on pin 3
61
//
62
#define LED_PIN       13
63
#define LED_PORT      PORTB
64
#define LED_BIT       7
65
#define PWM_PIN       3
66
#define PWM_VALUE     OCR3C
67
#define PWM_INTERRUPT TIMER3_OVF_vect
68
#else
69
//
70
// For modern ATmega168 and ATmega328 boards
71
//    Output is on pin 3
72
//
73
#define PWM_PIN       3
74
#define PWM_VALUE     OCR2B
75
#define LED_PIN       13
76
#define LED_PORT      PORTB
77
#define LED_BIT       5
78
#define PWM_INTERRUPT TIMER2_OVF_vect
79
#endif
80
81
// Smooth logarithmic mapping
82
//
83
uint16_t antilogTable[] = {
84
  64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109,
85
  54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341,
86
  45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968,
87
  38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768
88
};
89
uint16_t mapPhaseInc(uint16_t input) {
90
  return (antilogTable[input & 0x3f]) >> (input >> 6);
91
}
92
93
// Stepped chromatic mapping
94
//
95
uint16_t midiTable[] = {
96
  17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73,
97
  77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231,
98
  244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691,
99
  732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742,
100
  1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143,
101
  4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854,
102
  10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879,
103
  22121,23436,24830,26306
104
};
105
uint16_t mapMidi(uint16_t input) {
106
  return (midiTable[(1023-input) >> 3]);
107
}
108
109
// Stepped Pentatonic mapping
110
//
111
uint16_t pentatonicTable[54] = {
112
  0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346,
113
  411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288,
114
  3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306
115
};
116
117
uint16_t mapPentatonic(uint16_t input) {
118
  uint8_t value = (1023-input) / (1024/53);
119
  return (pentatonicTable[value]);
120
}
121
122
123
void audioOn() {
124
#if defined(__AVR_ATmega8__)
125
  // ATmega8 has different registers
126
  TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20);
127
  TIMSK = _BV(TOIE2);
128
#elif defined(__AVR_ATmega1280__)
129
  TCCR3A = _BV(COM3C1) | _BV(WGM30);
130
  TCCR3B = _BV(CS30);
131
  TIMSK3 = _BV(TOIE3);
132
#else
133
  // Set up PWM to 31.25kHz, phase accurate
134
  TCCR2A = _BV(COM2B1) | _BV(WGM20);
135
  TCCR2B = _BV(CS20);
136
  TIMSK2 = _BV(TOIE2);
137
#endif
138
}
139
140
141
void setup() {
142
  pinMode(PWM_PIN,OUTPUT);
143
  audioOn();
144
  pinMode(LED_PIN,OUTPUT);
145
}
146
147
void loop() {
148
  // The loop is pretty simple - it just updates the parameters for the oscillators.
149
  //
150
  // Avoid using any functions that make extensive use of interrupts, or turn interrupts off.
151
  // They will cause clicks and poops in the audio.
152
  
153
  // Smooth frequency mapping
154
  //syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;
155
  
156
  // Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...
157
  //syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));
158
  
159
  // Stepped pentatonic mapping: D, E, G, A, B
160
  syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));
161
162
  grainPhaseInc  = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2;
163
  grainDecay     = analogRead(GRAIN_DECAY_CONTROL) / 8;
164
  grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2;
165
  grain2Decay    = analogRead(GRAIN2_DECAY_CONTROL) / 4;
166
}
167
168
SIGNAL(PWM_INTERRUPT)
169
{
170
  uint8_t value;
171
  uint16_t output;
172
173
  syncPhaseAcc += syncPhaseInc;
174
  if (syncPhaseAcc < syncPhaseInc) {
175
    // Time to start the next grain
176
    grainPhaseAcc = 0;
177
    grainAmp = 0x7fff;
178
    grain2PhaseAcc = 0;
179
    grain2Amp = 0x7fff;
180
    LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite
181
  }
182
  
183
  // Increment the phase of the grain oscillators
184
  grainPhaseAcc += grainPhaseInc;
185
  grain2PhaseAcc += grain2PhaseInc;
186
187
  // Convert phase into a triangle wave
188
  value = (grainPhaseAcc >> 7) & 0xff;
189
  if (grainPhaseAcc & 0x8000) value = ~value;
190
  // Multiply by current grain amplitude to get sample
191
  output = value * (grainAmp >> 8);
192
193
  // Repeat for second grain
194
  value = (grain2PhaseAcc >> 7) & 0xff;
195
  if (grain2PhaseAcc & 0x8000) value = ~value;
196
  output += value * (grain2Amp >> 8);
197
198
  // Make the grain amplitudes decay by a factor every sample (exponential decay)
199
  grainAmp -= (grainAmp >> 8) * grainDecay;
200
  grain2Amp -= (grain2Amp >> 8) * grain2Decay;
201
202
  // Scale output to the available range, clipping if necessary
203
  output >>= 9;
204
  if (output > 255) output = 255;
205
206
  // Output to PWM (this is faster than using analogWrite)  
207
  PWM_VALUE = output;
208
}

: Bearbeitet durch User
von kopfkratzer (Gast)


Lesenswert?

kopfkratz
Da muß mehr geändert werden: Init, ISR, Timer PWM usw. usf.
Und wenn man schon dabei ist sollte man die Lookuptables gleich ins 
Flash legen ;-)
Hmm wo ist denn eigentlich "analogRead" definiert oder ist das ein 
"Sketch" ?
Dann das gesamte Projekt in der Arduino IDE laden und als Sourcecode 
exportieren, wenn's Tiny85 Arduinos geben sollte man den angeben :-P

von Karl H. (kbuchegg)


Lesenswert?

kopfkratzer schrieb:
> *kopfkratz*
> Da muß mehr geändert werden: Init, ISR, Timer PWM usw. usf.
> Und wenn man schon dabei ist sollte man die Lookuptables gleich ins
> Flash legen ;-)
> Hmm wo ist denn eigentlich "analogRead" definiert oder ist das ein
> "Sketch" ?

analogRead ist die Arduino Schereibweise für 'ADC an einem Kanal 
auslesen'.


Machbar ist das schon, die Arduino Sachen da rauszuoperieren. So viel 
wird davon ja nicht verwendet.
Die Timer Sachen müsste man mit den beiden Datenblättern in der Hand vom 
Mega8 zum Tiny45 transferieren - vorausgesetzt dass es dort jeweils 
vergleichbare Timer-Funktionalität gibt.

Ist halt Aufwand.

von Thomas E. (thomase)


Lesenswert?

Karl Heinz schrieb:
> Die Timer Sachen müsste man mit den beiden Datenblättern in der Hand vom
> Mega8 zum Tiny45 transferieren - vorausgesetzt dass es dort jeweils
> vergleichbare Timer-Funktionalität gibt.

Schon wieder dieser Scheiß-Mega8. Ja ich weiss, der beliebte Klassiker.

Die Einstellungen für den 328 kann man fast 1:1 für den Tiny45 
übernehmen.
1
  TCCR2A = _BV(COM2B1) | _BV(WGM20);
2
  TCCR2B = _BV(CS20);
3
  TIMSK2 = _BV(TOIE2);
4
5
  TCCR1A = _BV(COM1B1) | _BV(WGM20);
6
  TCCR1B = _BV(CS10);
7
  TIMSK = _BV(TOIE1);

Dann muß noch das Arduino Portgeraffel umgesetzt werden.

mfg.

von Eumel (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Schon wieder dieser Scheiß-Mega8. Ja ich weiss, der beliebte Klassiker.

Ist mir auch immer ein Rätsel wieso der an jeder Ecke verwendet wird. 
Aber wahrscheinlich gibts den noch wenn die anderen AVRs schon im Sarg 
liegen und sei es als Chinanachbau :)

von Thomas E. (thomase)


Lesenswert?

Shit! Timer 0 natürlich.

Thomas Eckmann schrieb:
> Karl Heinz schrieb:
>> Die Timer Sachen müsste man mit den beiden Datenblättern in der Hand vom
>> Mega8 zum Tiny45 transferieren - vorausgesetzt dass es dort jeweils
>> vergleichbare Timer-Funktionalität gibt.
>
> Schon wieder dieser Scheiß-Mega8. Ja ich weiss, der beliebte Klassiker.
>
> Die Einstellungen für den 328 kann man fast 1:1 für den Tiny45
> übernehmen.
>
1
>   TCCR2A = _BV(COM2B1) | _BV(WGM20);
2
>   TCCR2B = _BV(CS20);
3
>   TIMSK2 = _BV(TOIE2);
4
> 
5
>   TCCR1A = _BV(COM1B1) | _BV(WGM20);
6
>   TCCR1B = _BV(CS10);
7
>   TIMSK = _BV(TOIE1);
8
>
>
1
   TCCR2A = _BV(COM2B1) | _BV(WGM20);
2
   TCCR2B = _BV(CS20);
3
   TIMSK2 = _BV(TOIE2);
4
 
5
   TCCR0A = _BV(COM0B1) | _BV(WGM00);
6
   TCCR0B = _BV(CS00);
7
   TIMSK = _BV(TOIE0);

mfg.

von spess53 (Gast)


Lesenswert?

Hi

>Die Einstellungen für den 328 kann man fast 1:1 für den Tiny45
>übernehmen.

Timer1 ist aber beim ATMega328 ein 16-Bit- und beim ATTiny85 ein 
8-Bit-Timer. Und der hat auch ein paar Eigenheiten.

MfG Spess

von Thomas E. (thomase)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Die Einstellungen für den 328 kann man fast 1:1 für den Tiny45
>>übernehmen.
>
> Timer1 ist aber beim ATMega328 ein 16-Bit- und beim ATTiny85 ein
> 8-Bit-Timer. Und der hat auch ein paar Eigenheiten.
>
> MfG Spess

Zu spät. Hab ich schon korrigiert. Timer2 vom 328 ist bis auf den 
Prescaler identisch mit Timer 0 vom 85. Aber CS20 bzw. CS00 sind beide 
ohne Prescaler. Das passt.

mfg.

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Hey DANKE!
Hat super funktioniert. Die Arduino-Befehle konnte ich drin lassen, da 
ich die Arduino Umgebung zur Programmierung benutze. Was ich noch ändern 
musste:
-(nur für den Arduino Code): Ich musste andere Tiny-Core Dateien 
benutzen. Die sind hier zu finden: https://github.com/TCWORLD/ATTinyCore
Die Core Dateien von der Arduinoseite benutzen den Timer0 für die 
millis() Funktion. Das gab dann Konflikte mit der Interruptroutine.

-Die PWM-Ausgabe scheint nicht mit der uint_16 Variable zu 
funktionieren. Wenn ich den Wert kurz vor der Ausgabe in die Variable 
value schreibe, geht es.

-Ich musste den Code noch für einen Multiplexer anpassen, damit ich auch 
alle 5 Potis auslesen kann.

Funktioniert jetzt aber alles. Ist ein schönes kleines Geschenk, kann ja 
der mal selber probieren. Macht Spaß damit zu experimentieren.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Wenn es dir noch gelingt, die paar Sketch Aufrufe in normales C 
umzuwandeln und einen Plan zu zeichnen, wäre das doch ein schönes 
Paketchen für die AVR Projekte Ecke.
http://www.mikrocontroller.net/articles/Kategorie:AVR-Projekte

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.