Forum: Mikrocontroller und Digitale Elektronik 8bit ADC-Wert verarbeiten/umrechnen


von TBI (Gast)


Lesenswert?

Hallo liebes Forum.

Ich wollte ein altes Volume-Pedal um eine MIDI-Funktionalität erweitern.
Leider habe ich nach der Entwicklung auf dem Steckbrett mit einem Poti, 
welches den vollen Weg fahren kann, festgestellt dass das Pedal das 
nicht tut.
Bislang sieht mein Code so aus:
1
loop_adc:
2
  ;Referenzspannung AVCC - Ergebnis Linksbündig - Kanal 0
3
  ldi   TMP1, (0<<REFS1) | (1<<REFS0) | (1<<ADLAR) | (0<<MUX4) | (0<<MUX3) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0)
4
  out   ADMUX, TMP1
5
  ;Zähler laden
6
  ldi    TMP2, 8
7
  ;Ergebnisregister löschen
8
  clr    TMP3
9
  ;Zeiger setzen
10
  ldi    XL, LOW(adc0)
11
  ldi    XH, HIGH(adc0)
12
loop_adc_check:
13
  ;Vergleiche den Zähler mit 0
14
  cpi    TMP2, 0
15
  ;Wenn auf 0 springe zum Ende
16
  breq  loop_btn_right
17
  ;Den ADC starten
18
  sbi   ADCSRA, ADSC
19
loop_adc_load_wait:
20
  ;Überprüfen ob fertig konvertiert
21
  sbic  ADCSRA, ADSC
22
  ;Und Schleife neu starten falls noch nicht fertig konvertiert
23
  jmp    loop_adc_load_wait
24
  ;Das Low-Byte muss immer zuerst ausgelesen werden. Da drin stehen die 2 niederwertigsten Bits der Ergebnisses
25
  in    TMP3, ADCL
26
  ;Jetzt kommt das High-Byte. Das Low-Byte wird überschrieben um ein Zittern zu vermeiden. Wir haben also nur noch 8bit
27
  in    TMP3, ADCH
28
  ;Linksschieben, da wir nur 7 bit verarbeiten können
29
  lsr    TMP3
30
  ;Speichern des Ergebnisses und inkrementieren des Zeigers
31
  st    X+, TMP3
32
  ;Dekrementieren des Zählers
33
  dec    TMP2
34
  ;Inkrementieren des ADMUX-Registers
35
  inc    TMP1
36
  out    ADMUX, TMP1
37
  ;Schleife von neu beginnen
38
  jmp    loop_adc_check
39
    
40
loop_btn_right:
Den Befehl
1
  ;Linksschieben, da wir nur 7 bit verarbeiten können
2
  lsr    TMP3
muss ich nun durch eine Routine ersetzen, welche mir einem Eingangs-Wert 
von, sagen wir mal, 60 bis 150 in einen Wert von 0 bis 127 umrechnet.
Wie stelle ich das am besten an? Ich stehe da ein bissl aufm Schlauch.
Entwickelt wurde und wird das ganze auf einem ATMEGA32. Später soll 
alles auf einen Tiny25/45/85 portiert werden. Da hätte ich auch keinen 
Hardware-Multiplizierer.

Danke für Antworten und ein schönes Wochenende.

von Felix A. (madifaxle)


Lesenswert?

Ich würde da jetzt mit C anfangen. Dann sparst du dir den 
Assembleraufwand für die Multiplikation, Division und was noch so nötig 
wäre...

von Falk B. (falk)


Lesenswert?

Nimm C, BASCOM, Arduino oder sonstwas, da macht der Compiler die 
Multiplikation für dich. Oder frag den "8 Bit MSR Optimalspezialisten" 
Moby, ob er das für dich programmiert ;-)

von TBI (Gast)


Lesenswert?

Ich will hier keinen Thread über C vs. ASM vs. Bascom vs. was weiss ich.
Lediglich einen Denkanstoß, damit ich weiterkomme.

Danke.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

TBI schrieb:
> Später soll alles auf einen Tiny25/45/85 portiert werden. Da hätte ich
> auch keinen Hardware-Multiplizierer.
Aber genug Speicher: ich würde eine Tabelle mit 256 Einträgen anlegen 
und dort direkt mit dem AD-Wert den "umgerechneten" Midi-Wert abholen. 
Dann könntest du sogar irgendwelche Nichtlinearitäten ganz einfach 
geradebiegen...

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

@ TBI (Gast)

>Ich will hier keinen Thread über C vs. ASM vs. Bascom vs. was weiss ich.

Will auch keiner.

>Lediglich einen Denkanstoß, damit ich weiterkomme.

Nimm eine Softwarefunktion zur Multiplikation, die läuft auch auf den 
Tinys.
Die gibt es in einer App-Note von Atmel, musst du mal suchen.

von TBI (Gast)


Lesenswert?

Lothar M. schrieb:
> Aber genug Speicher: ich würde eine Tabelle mit 256 Einträgen anlegen
> und dort direkt mit dem AD-Wert den "umgerechneten" Midi-Wert abholen.

Ich wollte einen Kalibrierungstaster anbringen um die Minimalen und 
Maximalen Werte zu erhalten. Daher geht das leider nicht ohne weiteres.

von Michael U. (amiga)


Lesenswert?

Hallo,

hier würde ich auch eine Tabelle nehmen.
Tabellenstart in Z rein, ADC-Wert dazu addeiren und den MIDI-Wert 
abholen.
Kann man vor allem genau passend basteln oder in Excel berechnen oder 
wie auch immern. Macht man ja nur einmal.
Wenn man den Tiny85 nimmt kann man das vermutlich auch beim Start 
berechnen und im Ram anlegen, dann spielt hinterher Laufzeit auch keine 
Rolle mehr.

Gruß aus Berlin
Michael

von TBI (Gast)


Lesenswert?

Ich bräuchte 256 * 256 (Minimaler Wert) * 256 (Maximaler Wert) = 16MByte
Selbst bei 7 bit wären das noch 2MByte. Das packt auch der größte Mega 
nicht...

von Tom (Gast)


Lesenswert?

TBI schrieb:
> Wie stelle ich das am besten an?

Ob es am besten für dich ist, weiss ich nicht, aber üblicherweise würde 
man eine lineare Umrechnung der Form y =x * m + b machen. In deinem Fall 
also als Ganzzahlrechnung mit y = x * m1 / m2 + b mit z.B. m1=361, 
m2=256 und b=-84. Das spart dir schon mal Float-Rechnung und Division.

> Da hätte ich auch keinen Hardware-Multiplizierer.
Dann wirst du die Multiplikation wohl in Software, ggf. hart kodiert mit 
Addieren und Schieben machen müssen, also
1
(((((x << 2) + x) << 1) + x) << 2) + x) << 3) + x

von Felix A. (madifaxle)


Angehängte Dateien:

Lesenswert?

Du müsstest, wenn du kalibrieren musst, die Geradengleichung im 
Controller bestimmen.

Wie im Bild zu sehen wären die Werte 60 und 150 beispielhaft die 
Ergebnisse aus der Kalibrierroutine. Diese musst du im Gleichungssystem 
benutzen, um die Faktoren für die Gerade zu erhalten.

von Noch einer (Gast)


Lesenswert?

> Hardware-Multiplizierer ?

Jeder Tiny dürfte die Skalierung schneller berechnen, als dein Kabel die 
Midi-Pakete transportieren kann.

von TBI (Gast)


Lesenswert?

Super. Damit komme ich weiter.

Vielen Dank und ein schönes Wochenende.

von TBI (Gast)


Lesenswert?

Noch einer schrieb:
> Jeder Tiny dürfte die Skalierung schneller berechnen, als dein Kabel die
> Midi-Pakete transportieren kann.

Genau das denke ich jetzt auch.

Erneut... Vielen Dank

von Thomas E. (picalic)


Lesenswert?

Servus,

in meinem alten picalic-Projekt benutze ich eine lineare Interpolation, 
um den Y-Wert einer Geraden für einen bestimmten X-Wert (im Bereich 
X=0..X2) zu bestimmen. Einen Offset auf die X-Werte (wenn X1 nicht Null 
sein soll) kann man ja leicht durch Addition erschlagen.

Ist zwar PIC-Assembler, aber vielleicht als Denkanstoss...:
1
;================ Linear Interpolation
2
;computes Y-value for given X-val. 
3
;input:
4
;y1 = Y-value for X=0
5
;y2 = Y-value for X=x2
6
;x2 = X-value: length of interval
7
;x = x input value (1 ... x2-1)
8
;output:
9
;y = Y-value for given x
10
11
interpolate
12
  clrf  x1
13
  btfsc  x2,7
14
  goto  interp_loop  ;x2 >= 0x80
15
16
  bcf  STATUS,C
17
interp_max        
18
  rlf  x2,F    ;maximize for higher precision
19
  rlf  x,F
20
  btfss  x2,7
21
  goto   interp_max
22
23
interp_loop
24
  movf  y1,W
25
  addwf  y2,W
26
  movwf  y
27
  rrf  y,F    ;= mean val. of y1, y2
28
29
  movf  x1,W
30
  addwf  x2,W
31
  movwf  temp    
32
  rrf  temp,F    ;= mean val. of x1, x2
33
  movf  temp,W
34
  subwf  x,W
35
  btfsc  STATUS,Z
36
  return      ;exact match -> iteration complete
37
38
  movf  y,W
39
  xorwf  y1,W
40
  btfsc  STATUS,Z  ;y == y1?
41
  goto  interp_exit  ;Y: result is y1 or y2
42
43
  movf  y,W
44
  xorwf  y2,W
45
  btfsc  STATUS,Z  ;y == y2?
46
  goto  interp_exit  ;Y: result is y1 or y2
47
 
48
  movf  temp,W    ;mean(x1,x2) -> W
49
  btfss  STATUS,C
50
  goto  interp_less  ;C=0 => x < mean(x1,x2) 
51
;x > mean(x1,x2):
52
  movwf  x1    ;new interval = mean..x2
53
  movf  y,W
54
  movwf  y1    ;new y within y..y2
55
  goto  interp_loop
56
57
;x < mean(x1,x2):
58
interp_less
59
  movwf  x2    ;new interval = x1..mean
60
  movf  y,W
61
  movwf  y2    ;new y within y1..y
62
  goto  interp_loop
63
64
;difference(y1,y2) <= 1, use y1 or y2 depending on x < or > mean(x1,x2)
65
interp_exit
66
  movf  y1,W
67
  btfsc  STATUS,C
68
  movf  y2,W    ;x > mean(x1,x2)
69
  movwf  y
70
  return

von Jobst M. (jobstens-de)


Lesenswert?

Bei Werten von 60 bis 150 würde ich zunächst die 60 abziehen.
Bleibt 0 - 90
Dies würde ich mit 182 multiplizieren und vom Ergebnis die Bits 7-14 
benutzen.

Natürlich kannst Dur diese Werte auch nachträglich kalibrieren.
Unterster Wert ist derjenige, der abgezogen wird.
Und 16352 geteilt durch das was nach Abzug vom obersten Wert übrig 
bleibt ist die Zahl, mit der multipliziert werden muss.


Gruß

Jobst

von Simpel (Gast)


Lesenswert?

Ich würde schauen, dass ich das Poti im Pedal so verdrehe, dass es 0-90 
Verstellweg hat und eine ext. ADC-Referenz erzeugen, welche dem 
Spannungswert bei 90 entspricht. Dann hast du von 0-90 den vollen 
ADC-Bereich von 0-255, machst einen Rechtsshift und hast 0-127.

von TBI (Gast)


Lesenswert?

Ich werde die Routine neu schreiben und dabei den jeweiligen MIDI-Wert 
aus dem ADC-Wert live berechnen. Selbst ein Tiny25 hat dazu genug Kraft.

Den im Ursprungspost geposteten Quellcode habe ich auf einen Kanal 
reduziert und das Linksschieben verworfen. Jetzt bekomme ich zwar im 
Moment nur Blödsinn, aber morgen werde ich mich mal in die 
SW-Multiplikation und Division einlesen. Dann sollte das auch ganz 
schnell klappen.

Ich habe das erstmal so vor:
TMP1 = ADC - Kalibrierung(Min)
TMP1 = TMP1 * 127
TMP2 = Kalibrierung(Max) - Kalibrierung(Min)
TMP1 = TMP1 / TMP2 (Rest wird verworfen)

Ob die Register gerade in Benutzung (bzgl. des geposteten SC) sind, oder 
ob ich nachher noch den Rest der Division nutzen muss stelle ich fest 
während/wenn ich das gecoded und ausprobiert habe. Aber erst morgen. Ein 
Problem nach dem anderen...

Ich bedanke mich für alle Antworten und wünsche einen schönen restlichen 
Sonntag.

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.