ATMega Code auf ATTiny portieren

Gast #3411312
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
}
Gast #3411542
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
#3411555
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.
#3411595
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.
#3411608
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.
#3411620
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.
Gast #3413394
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren