hallo erstmal,
ich bin grad dabei mit einem ATmega88 mit 18,432 MHz einen magnetencoder
auszuwerten. es handelt sich dabei um den AS5040. ich betreibe ihn als
inkrementalsensor, sprich ich will nur die Spuren A und B mit meinem µC
auslesen. auslösung in diesem modus ist 8 bit = 256 inkremente.
ich muss 2 encoder einlesen und sowohl drehzahl als auch drehrichtung
bestimmen. ich kann aber pro encoder nur einen einzigen timer/counter
benutzen. um es etwas einfacher zu beginnen will ich erstmal nur die
daten eines encoders auswerten, was in meinem fall encoder 2 ist.
angeschlossen ist er so:
ENC2:
Spur A geht auf PB1
Spur B geht auf PB0
ich komme also um einen pin change interrupt nicht herum, um drehzahl
und drehrichtung zu bestimmen. aber das dürfte kein problem sein, da ich
recht saubere signale bekomme.
im pin change interrupt frage ich die drehrichtung ab und zähle bei
jedem high-wert (steigende flanke) meine inkremente um eins hoch. die
drehrichtung hab ich dadurch dass Spur B entweder 0 oder 1 ist wenn Spur
A 1 ist.
die drehzahl bestimme ich mit dem timer, den ich auf einen takt von 1
sekunde eingestellt hab. bei jeder sekunde wird ein interrupt ausgeführt
der ein flag auf 1 setzt, welches ich in meiner hauptschleife abfrage.
so kann ich die umdrehungen pro sekunde bestimmen, die ich dann per I2C
an einen anderen ATmega88 schicke. dieser wiederrum sendet meine
drehzahl und drehrichtung per UART an meinen PC.
mein problem ist, dass der interrupt für den sekundentakt zwar ohne
probleme ausgeführt wird, der pin change interrupt aber keinen mucks
vonsich gibt.
ich hoffe ihr könnt mit helfen.
hier der code:
1
#include <avr\io.h> // Header-Datei für I/O Konfiguration (intern werden weitere Dateien zugefügt)
2
#include <avr\interrupt.h> // Header-Datei für globale Interrupt Aktivierung
3
#include <stdlib.h>
4
#include <stdint.h> // Header-Datei zur Definition der Zahlentypen (int, char, unsigned char usw.)
5
#include <stdbool.h> // Header-Datei zur Definition der 1-Bit-Variablen bool
6
#include "i2c.h"
7
#include "defines.h"
8
#include "pwm.h"
9
10
11
#define PHASE_A2 (PINB & 1<<PB1) // Channel A of Enc 2
12
#define PHASE_B2 (PINB & 1<<PB0) // Channel B of Enc 2
mit der funktion: void PCI_init(void)
direkt unter der main.
aber ich seh grad da steckt ja ein fehler drin. das müsste anstatt:
PCMSK0 |= (1<<PCINT0);
PCMSK0 |= (1<<PCINT1); heißen.
tatsache, hast recht, hab ich total übersehn. aber leider hat sich
nichts geändert. hab immernoch das problem dass das programm garnicht in
den pin change interrupt springt.
hab ein bissle rumexperimentiert.
irgendwie kommen sich die beiden interrupts in die quere. anscheinend
scheint der pin change interrupt doch irgendwie zu funktionieren obwohl
er den code nicht ausführt.
sobald ich den angeschlossenen motor stoppe und somit keinen pin change
interrput mehr habe läuft mein 1 sekundentakt. lass ich den motor wieder
laufen, oder drehe das rad per hand greift der pin change interrupt
wieder und mein 1 sekundentakt wird jetzt nicht mehr ausgeführt.
irgendwie seltsam.
hier nochmal der code:
1
voidPCI_init(void)
2
{
3
DDRB&=~(1<<PB1);
4
DDRB&=~(1<<PB0);
5
PCICR|=(1<<PCIE0);
6
PCMSK0|=(1<<PCINT1);
7
}
sorry wegen meinem ersten beitrag, hab nicht gesehn das es auch einen
extra c-code tag gibt.
Thomas schrieb:
> scheint der pin change interrupt doch irgendwie zu funktionieren obwohl> er den code nicht ausführt.
Vielleicht liegts an fehlenden Klammern: (PINB & (1<<PB0))
Ich weiß gerade nicht, wie die Operatorenwertigkeit ist.
habs grad ausprobiert. daran liegts leider auch nicht.
ich nehme mal an dass die interrupts auch keine zulange bearbeitungszeit
haben, zumal ich da eigentlich drauf geachtet hab. echt ein merkwürdiges
verhalten.
Also zeitlich behindern sich die Interrupts nicht, dafür sind sie zu
kurz. Und selbst wenn beide "gleichzeitig" feuern, wird sich das vom
Controller gemerkt. Du verlierst also keinen Interrupt.
Nimm doch mal deine Toggle-LED und setze sie in die verschiedenen
if-Abfragen deiner PCINT-ISR. Dann kannst du recht schnell sehen, was
ausgeführt wird und was nicht.
Toggeln macht man übrigens so:
also wenn ich das rad nicht drehe, nicht per pwm und nicht per hand,
dann funktioniert der 1 sekundentakt laut toggle-led. auch die
dazugehörige funktion in der main funktioniert.
hab versucht nur die led im pin change interrupt einzuschalten, nichtmal
das funktioniert. und wie gesagt, sobald ich das rad drehe, wird mein 1
sekundentakt unterbrochen und damit auch das blinken der led. lass ich
das rad wieder stillstehn, so läuft mein 1 sekundentakt wieder.
ja den artikel kenne ich. hab es mit deinem programm auch schon
versucht, allerdings bekomm ich damit keine drehzahlbestimmung hin.
meine maximale drehzahl liegt bei 6 kHz.
@ Thomas (Gast)
>das funktioniert. und wie gesagt, sobald ich das rad drehe, wird mein 1>sekundentakt unterbrochen und damit auch das blinken der led. lass ich>das rad wieder stillstehn, so läuft mein 1 sekundentakt wieder.
Und das wundert dich?
>unsigned int impulse = 0, impulse_second = 0;
impulse wird auch im Interrupt verwendet, es muss volatile sein.
> cli();> impulse_second = impulse;> sei();> impulse = 0;
Das ist KEIN Atomarer Zugriff. Das Löschen muss auch vor dem sei()
passieren, siehe Interrupt.
> cli();> HiChar = (unsigned char) (impulse_second >> 8);> LoChar = (unsigned char) (impulse_second & 0xff);> i2c_write_slave(HiChar);> i2c_write_slave(LoChar);> i2c_write_slave(direction);> sei();
Das ist vollkommen NOGO!!! Warum steckst du die I2C Sachen VOR das
sei()? Damit bremst du deine Interrupts ZIEMLICH lange aus.
>ISR(PCINT1_vect)>{> PORTB &= ~(1<<PB2); // LED1 toggeln zum debuggen> if(PHASE_A2)> {> impulse++;> }> if((PHASE_A2 == 1) & (PHASE_B2 == 0)) // Drehrichtung vorwärts> {> direction = 0;> }> if((PHASE_A2 == 1) & (PHASE_B2 == 1)) // Drehrichtung rückwärts> {> direction = 1;> }
Das Thema ist auch alt. Einen Drehgeber wertet man so NICHT aus. Die
CPU wird bei dir wahrscheinlich durch Prellen mit Interrupts überflutet.
>ISR(TIMER1_COMPA_vect) // Modul Taktgenerator: Aufruf jede Sekunde>{> s = 1;> TCNT1 = 0; // Timer 1 auf null>}
Macht man so nicht, auch wenn es hier funktioniert. Dafür nutzt man den
CTC Modus.
MfG
Falk
Hast du mal drüber nachgedacht, wie lange in deinem Programm die
Interrupts tatsächlich freigegeben sind?
Ich würde mal sagen, weniger als 0.1% der verfügbaren Zeit. Das kann so
nicht funktionieren.
Oliver
vielen dank für die hilfe :)
@falk
hab deine tipps umgesetzt. die variable "impulse" hab ich als volatile
deklariert.
ich hoffe ich hab den rest richtig verstanden, darum post ich den
veränderten code nochmal:
1
if(s==1)
2
{
3
// PORTB ^= (1<<PB2); // LED1 toggeln zum debuggen
4
5
cli();
6
impulse_second=impulse;
7
impulse=0;
8
s=0;
9
sei();
10
}
11
12
cli();
13
HiChar=(unsignedchar)(impulse_second>>8);
14
LoChar=(unsignedchar)(impulse_second&0xff);
15
sei();
16
17
i2c_write_slave(HiChar);
18
i2c_write_slave(LoChar);
19
i2c_write_slave(direction);
das stimmt natürlich, die drehrichtung werde ich noch ändern. aber das
ist vorerst nicht so das problem. mein problem ist einfach dass ich gar
nicht in den pin change interrupt hinein komme.
@Oliver
ich verstehe nicht ganz was du meinst. ich hab jetzt lediglich 2
situationen in denen ich die interrupts sperre, weil es wichtig ist dass
ich die richtigen werte übertragen kann. müsste doch genug zeit für die
interrupts zur verfügung stehen?!
>ich hab jetzt lediglich 2>situationen in denen ich die interrupts sperre, weil es wichtig ist dass>ich die richtigen werte übertragen kann. müsste doch genug zeit für die>interrupts zur verfügung stehen?!
Jetzt ja, vorher nicht. Vorausgesetzt natürlich, die i2c-Funktionen
sperren die Interrupts nicht auch sofort wieder...
Im übrigen ist hier
1
cli();
2
HiChar=(unsignedchar)(impulse_second>>8);
3
LoChar=(unsignedchar)(impulse_second&0xff);
4
sei();
cli und sei auch noch überflüssig, da impulse_second ja niemals in einer
ISR benutzt wird. Da darf das ruhig unterprochen werden.
Oliver
Ähem,...
>ENC2:>Spur A geht auf PB1>Spur B geht auf PB0>The pin change interrupt PCI0>will trigger if any enabled PCINT7..0 pin toggles.>ISR(PCINT1_vect)
Das passt irgednwie nicht so richtig zusammen ;-)
Oliver
versteh ich net ganz, sorry :)
ich bin hiernach vorgegangen:
"Each PCINT7..0 bit selects whether pin change interrupt is enabled on
the corresponding I/O
pin. If PCINT7..0 is set and the PCIE0 bit in PCICR is set, pin change
interrupt is enabled on the
corresponding I/O pin. If PCINT7..0 is cleared, pin change interrupt on
the corresponding I/O pin
is disabled."
das is die beschreibung des PCMSK0-Registers. da ich nur den PCI an PB1
aktiviert habe, stimmt doch der PCI handling vector. alle anderen sind
ja abgeschaltet.
moin moin,
sorry, konnte gestern abend nicht mehr weiter testen, musste leider zur
nachtschicht :)
ich habs mit "ISR(PCINT0_vect)" mal versucht und es funktioniert
wunderbar. genauso wie es soll. jetzt muss ich nur noch die
drehrichtungserkennung verbessern.
vielen dank an euch :)
allerdings versteh ich nicht ganz warum jetzt grad PCINT0_vect
funktioniert. ich hab doch explizit PCINT1 an PB1 freigegeben und auch
über den zugehöriger handler angesprochen. müsste er jetzt nicht
eigentlich jeden PCI am pin PB0 erkennen?
@ Thomas (Gast)
>allerdings versteh ich nicht ganz warum jetzt grad PCINT0_vect>funktioniert. ich hab doch explizit PCINT1 an PB1 freigegeben
;-)
Hehe, böse Falle. PCINT1 bezeichnet hier ein Bit im Register PCMSK0,
mämlich Bit#1.
Der Interruptvector PCINT0_vect ist für diese Register zuständig, also
die Bits PCINT0..7
Da hat Atmel mit der Namensgebung etwas daneben griffen, ein und der
selbe!!! Name für zwei verschiedne Dinge!
MFG
Falk
ach so ist das. haben die wohl eingebaut um die leute zu ärgern ;)
ich hab mich einfach nach der pin-übersicht des ATmega88 auf seite 2 des
datenblattes gerichtet. da steht dass PCINT1 zu PB1 gehört, aber wenn
das nur das register beschreibt is das ganz schön irreführend.
nochmals danke, war schon echt am verzweifeln :)