PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Inkrementalgeber/Magnetgeber auswerten



Bucki
30.06.2017, 12:41
Hallo liebe RoboterNETZ Gemeinde,

momentan möchte ich gern einen Magnetgeber mit der Auflösung von 2 auswerten. Dazu benutze ich den Atmega88p und benutze HTerm um mir die Werte auszugeben. Nur leider funktioniert das noch nicht ganz. :-k
Ich möchte das Ganze so gestalten, dass ich diesem eine Messdauer von bspw. 200ms vorgebe. ( Da sehr geringe Auflösung, aber dann noch recht gut geregelt werden soll, wobei die Programmdauer nicht sehr kritisch ist ) Während der Messdauer soll der 8-Bit Timer 0 also laufen und beide externene Interrupteingänge INT 0 und INT 1 ( sind also 2 Geber ) sollen bei einer steigenden Flanke einen Impuls weiter zählen.
Die 200ms habe ich versucht über einen Prescaler + mehrere Overflows einzustellen. Nur leider zeigt mein H-Term nur Kauderwelsch an, also so als würde es die Zeichen nicht interpretieren können. ( einmal Endekennung '\0' ) Es kommt auch nur ein einziges Mal ein Zeichen und nicht zyklisch wie die While Schleife es eigentlich machen söllte.

( Die Richtungsauswertung kommt erst im nachhinein, wenn wenigstens das funktioniert )


#define F_CPU 16000000UL

#include <avr/io.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define Baud 9600UL

volatile uint16_t DRZ1, DRZ2;
volatile uint8_t TIM0_Overflow, Drehrichtung1, Drehrichtung2;

void UARTinit(){

UBRR0 = 103; // Baudrate laut Datasheet
UCSR0B = (1<<TXEN0); // Enable Senden
UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); // Setze 8 Bit Datenlänge

// Alles was 0 ist setzt Asynchron Modus, Normal Speed, 1 Stop Bit und keine Paritaet
}

int uart_send( unsigned char c)
{
while (!(UCSR0A & (1<<UDRE0)))
{
// mach nichts
}
//senden
UDR0=c;
return 0;
}

void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_send(*s);
s++;
}
}

ISR(INT0_vect)
{
DRZ1++;
}

ISR(INT1_vect)
{
DRZ2++;
}

ISR(TIMER0_OVF_vect)
{
TIM0_Overflow++;
}

uint16_t Inkrementalgeber(uint16_t* Drehzahl1, uint16_t *Drehzahl2)
{

TCNT0 = 0;
DRZ1 = 0; // Alle Zählerwerte auf 0 zurücksetzen
DRZ2 = 0;
TIM0_Overflow = 0;
TCCR0B |= (1<<CS02) | (1<<CS00); // Frequentvorteiler von 1024 --> 16MHz/1024 = 64us pro Tick UND TImer0 starten
// 200ms/64us = 3125 --> 8 Bit Timer = 255 --> 12 * 255 + 1 * 65 = 3125 --> TIM0_Overflow = 12 und TCNT0 = 65, damit mit 8 Bit Timer und Prescaler 200ms Messzeit erreicht
TIMSK0 |= (1<<TOIE0); // Overflow_Flag enablen
EICRA |= (1<<ISC11) | (1<<ISC01) | (1<<ISC00) | (1<<ISC10); // INT0 und INT1 auf steigende Flanke triggern
EIMSK |= (1<<INT1) | (1<<INT0); // INT1 und INT0 Interrupts enablen
sei(); // Globale Interrupts erlauben

while (1)
{
if (TIM0_Overflow >= 12)
{
if (TCNT0 >=65)
{
break;
}

}
// tue nichts bis 200ms abgelaufen sind
}

TCCR0B &= ~((1<<CS02) | (1<<CS00)); // Timer stoppen

cli(); // Globale Interrupts ausschalten

*Drehzahl1 = DRZ1; // Werteübergabe der Drehzhalen an das Hauptprogramm
*Drehzahl2 = DRZ2;

}

int main(void)
{
uint16_t Drehzahl1, Drehzahl2;
uint8_t KanalA, KanalB;

DDRD &= ~((1<<PIND2) | (1<<PIND3)); // INT0 und INT 1 als Kanaleingänge für beide Inkrementalgeber auswählen
DDRD |= (1<<PIND1); // UART TX als Ausgang
DDRD &= ~(1<<PIND0);

TCCR0A &= ~((1<<WGM00) | (1<<WGM01)); // Normal Operation Mode für Timer0 auswählen
TCCR0B &= ~(1<<WGM02);

char s[30];
uint16_t x;

while (1)
{
Inkrementalgeber(&Drehzahl1 , &Drehzahl2);

Drehzahl1 = 150 * Drehzahl1; // Drehzahl in 1/min = 60s * ( 1000ms / Messdauer[ms] ) * ( Anzahl Flanken / Imp. pro Umdrehung [ Auflösung ] )
Drehzahl2 = 150 * Drehzahl2; // 60s * 1000/200 * DRZ/2 = 150*DRZ

KanalA = Drehzahl1 / 72; // Untersetzung Getriebe einbringen
KanalB = Drehzahl2 / 72;

x=KanalA;
sprintf(s, "Drehzahl 1: %.d 1/min \n", x);
uart_puts( s );

//_delay_ms(200);

x=KanalB;
sprintf(s, "Drehzahl 2: %.d 1/min \n", x);
uart_puts( s );

//_delay_ms(200);
}
}

Vielen Dank schonmal für eure Hilfe!

Grüße,
Bucki

021aet04
30.06.2017, 20:16
Pullup Widerstände?

Mfg Hannes

Bucki
03.07.2017, 08:05
Weiß ich jetzt nicht genau wie das gemeint ist. :-k Brauch ich an der Stelle doch nicht, wenn ich die High Flanke auswerten möchte oder? Dann doch eher Pull Down oder?

MfG Bucki

oberallgeier
03.07.2017, 08:54
Pullup Widerstände? ..
.. Brauch ich an der Stelle doch nicht, wenn ich die High Flanke auswerten möchte oder? Dann doch eher Pull Down oder? ..Wenn Du das nicht brauchst, ists doch ok - da hatte Dir sicher das Datenblatt des Encoders Auskunft gegeben. Oder verlangt der Encoder (sprich: das Datenblatt) nen Pull Down?

Ich habe die Auswertung für die Encoder (https://www.pololu.com/product/3081) an meinem Selbstbau"Servo" (https://www.roboternetz.de/community/threads/61379-Kopfsache-und-ein-m1284-etliche-Servos-viel-Alu?p=625271&viewfull=1#post625271) mit Pullup gemacht. Die Auswertung wirst Du sicher anders machen, ist hier sehr speziell. Kann aber, wie erwähnt, für Deine Encoder anders verlangt werden, siehe zugehöriges Datenblatt. Belegt werden soll hier nur die Initialisierung der Pinne mit internem PullUp :
Initialisierungen der Controllerpinns im main
/* >>
Stand ...\C4\C4\SVbby2\SVbby1p.c
================================================== ============================= =
Target MCU : ATmega 328p QFM
Target Hardware : babyorangutan und Micromotor als Servo mit Standardfunktion
Target cpu-frequ. : 20 MHz Resonator
================================================== ============================= =
. . . . Code gestrichen
================================================== ============================ */
. . . . Code gestrichen
// ================================================== =========================== =
// === HAUPTProgramm ================================================== ======== =
. . . . Code gestrichen
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// === Grundlegende Initialisierungen der Hardware, Portdefinition (PDIP !!)
// PCINT14,/RESET,PC6 1 28 PC5,ADC5,SCL,PCINT13
// PCINT16,RxD,PD0 2 27 PC4,ADC4,SDA,PCINT12
// PCINT17,TxD,PD1 3 26 PC3,ADC3,PCINT11
// PCINT18,INT0,PD2 4 25 PC2,ADC2,PCINT10
// PCINT19,OC2B,INT1,PD3 5 24 PC1,ADC1,PCINT9
// PCINT20,XCK,T0,PD4 6 23 PC0,ADC0,PCINT8
// VCC 7 22 GND
// GND 8 21 AREF
// PCINT6,XTAL1,TOSC1,PB6 9 20 VCC
// PCINT7,XTAL2,TOSC2,PB7 10 19 PB5,SCK,PCINT5
// PCINT21,OC0B,T1,PD5 11 18 PB4,MISO,PCINT4
// PCINT22,OC0A,AIN0,PD6 12 17 PB3,MOSI,OC2,OC2A,PCINT3
// PCINT23,AIN1,PD7 13 16 PB2,/SS,OC1B,PCINT2
// PCINT0,CLKO,ICP1,PB0 14 15 PB1,OC1A,PCINT1
// - - - - - - - - - - - - - - -
// Initialisierung der BOARD!! Anschlüsse am babyorangutan mit mega328-TQFP:
// /RESET, PC6 1 EU A 28 PC5 ============
// UART-RxD, PD0 2 EU A 27 PC4 ====
// rtUserLED, UART-TxD, PD1___3 EU A 26___PC3
// Encoder-1-A, INT0, PD2 4 EU A 25 PC2, TASTEJ-GND
// Motor2B, OC2B, PD3 5 EU EU 24 PC1, TASTEJ-In = PCINT9
// Encoder-1-B, PCINT20, PD4___6 EU A 23___PC0
// +++ VCC 7 + - 22 GND ---
// --- GND 8 - + 21 AREF ref
// XTAL1 PB6___9 A 20___AVCC
// XTAL2 PB7 10 A A 19 PB5, SCK, PCINT5
// Motor1B, OC0B, PD5 11 EU A 18 PB4, MISO, (GND)-blaue-LED
// Motor1A, OC0A, PD6__12 A A 17___PB3, MOSI, OC2A, Motor2A
// Taste rechts, PD7 13 A A 16 PB2, (+)-blaue LED
// (+)-heartbeat, PB0 14 A A 15 PB1, (GND)-Heartbeat-LED
// Anmerkungen zu den Motoren: die Controllerpins sind NUR über
// sind NUR über den Motortreiber (Wahrheitstabelle!!) zu erreichen
// - - - - - - - - - - - - - - -
// Ports als Ein- (0) oder Ausgänge (1) konfigurieren, Pins/Pull Ups (1) aktiv.
// A = Ausgang, E = Eingang ohne , EU = Eingang MIT PullUp, Belegung siehe oben
DDRB = 0b11101111; // Heartbeat auf B0(+)/(GND -)
PORTB = 0b00010000; // PB5: PCI für Servopuls
//
DDRC = 0b00111101; // PC0, PC2-5, PC6+7-Pin H I E R bei babyorangutan (MLF)
PORTC = 0b11000010; // PC0, PC2-5 sind aktuell Ausgänge ##>> KEIN Pullup !!
//
DDRD = 0b01101011; // RX/TX-PD0/1, PD5/PD6 = M1B/M1A, PD7 = Taste: PullUP !
PORTD = 0b10010100; // Pull Ups aktivieren, mit pololuMot AUCH bei INT0/~1
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
. . . . Code gestrichen
Build succeeded with 0 Warnings...
================================================== ========== */

Initialisierungen und Auswertung der Encodereingänge
/* >>
Stand ...\C4\SVbby1p\Sbby1p_motij.c
================================================== ============================= =
. . . . Code gestrichen
================================================== ============================= =
*** Versionsgeschichte:
====================
x24 23Nov16 1450 Motor EINschalten erst nach Einstellung der "Hand auf für Geld"
. . . . Code gestrichen
================================================== ============================ */


// ================================================== =========================== =
// === Initialisierung externer Interrupts bei m328, doc 8271I
// EXT_INT0 auf PORTD2 -- Encoder IencB0 auf PIND2
// ================================================== =========================== =
void XTI0_int( void ) // Initialisiere Interrupt 0, PD2, auf ANY edge
{ // => EICRA ISC00 + ~10 auf 1 S71
// - - - - - - - - - - - - - - - -
// Initialisiere PD2 als externen Interrupt, ANY edge
EICRA |= (1<<ISC00); // INT0 triggert auf jede Flanke (any edge)
EIMSK |= (1<<INT0); // erlaube INT0
// Initialisierung der Zeiten:
Iencdr0 = Iz_diff0 = Iz_yseci0 = Iz_ysecv0 = 0;
tmrE0 = 0; // Timer für Drehzahlerfassung
//
Iencdr0 = Iencdef0; // Initialisiere Startwert Encoder (vgl. main)
IencB0 = IenBdef0; // Initialisiere Startwert Encoder
//
} // Ende von void XTI_0_1_init( void )
// ================================================== =========================== =


// ================================================== =========================== =
// === Nicht unterbrechbare ISR für EXT_INT0(any edge auf mega328 ======== =
// Der Timer tmrE0 wird ausgelesen für Laufzeit des EXT_INT0 auf PD2.
// Enc-zähler Iencdr0 wird mit jedem Tick hochgezählt, ohne Richtung=any edge
// Encoder IencB0 wird richtungskonform hochgetickert => gesamte Fahrstrecke
// seit Rücksetzen (irgendwo); Aussage über Drehrichtung ist gegeben
// EXT_INT0 (Enc-spurA) triggert auf PORTD 2
// PCINT20 (ENC-spurB) triggert auf PORTD 4
// ================================================== =========================== =
ISR(INT0_vect) // INT0-PD2 triggert ANY edge => lesen Iencdr0/A
{ // => Bei Aufruf PORTD2 prüfen auf high/low
// - - - - - - - - - - - - - - - -
//251116 if ( Isecstrt ) return; // Keine Messung in der Startphase
// ToggleBit ( PBLED, LBb ); // blLED toggeln, PORTB, blLED auf PB2
Iz_diff0 = tmrE0; // Hier die Zeit (in x 50µs-tupsi) seit letztem ISR-Aufruf
tmrE0 = 0; // Timer für Encoderinterupts auf Null
Iencdr0 ++; // Incrementiere Encodercounter, NUR aufwärts
if ((IsBitSet (PIND, 2)) == (IsBitClr (PIND, 4))) IencB0++;
// Rad treibt vorwärts, math. negativ
else IencB0--; // Rad treibt rückwärts, math. positiv
} // Ende ISR(INT0_vect)
// ================================================== =========================== =


// ================================================== =========================== =
// === Initialisierung Pin Change Interrupt bei m328, doc 8271I
// PCINT[23:16], PORT D4, any Edge <=> Für Zählung Encoderflanken Spur "B"
// Die zugehörige ISR tickert Iencdr0 und IencB0 rauf bzw. runter
// ==>> Die Zeit-/Drehzahlerfassung bleibt NUR bei EXT_INT0/PB2
// ================================================== =========================== =
void PCI2_int( void ) // Initialisiere PCI 20, PB4, any edge doc 74
{ //
// - - - - - - - - - - - - - - - -
//251116 if ( Isecstrt ) return; // Keine Messung in der Startphase
PCICR |= ( 1<<PCIE2 ); // Auswahl PCINT[23:16] ; hier PD4; siehe PCMSK2
// PCI triggert auf jede Flanke (any edge)
PCMSK2 |= ( 1<<PCINT20 ); // Interrupt kommt von PB5
} // Ende von void XTI_0_1_init( void )
// ================================================== =========================== =


// ================================================== =========================== =
// === Nicht unterbrechbare ISR für PCINT0 auf PB5; mega328
// PCINT[7:0], PORTB5, any Edge <=> Für Pulszeitmessung Servopuls
// Die zugehörige ISR misst die Pulszeit tPULS für einen Servo in [tupsi]
// also die Zeit, in der der Servopuls auf high steht
// Siehe dazu ISR(TIMER1_COMPA..
// ================================================== =========================== =
ISR(PCINT2_vect) // PCINT2 triggert ANY edge => lesen Iencdr0/B
{ // prüfe ob PB5 = high, nur dann Wert übernehmen
// - - - - - - - - - - - - - - - -
//251116 if ( Isecstrt ) return; // Keine Messung in der Startphase
// ToggleBit ( PBLED, LBb ); // blLED aus, PORTB, blLED auf PB2
Iz_diff0 = tmrE0; // Hier die Zeit (in x 50µs-tupsi) seit letztem ISR-Aufruf
tmrE0 = 0; // Timer für Encoderinterupts auf Null
Iencdr0 ++; // Incrementiere Encodercounter, NUR aufwärts
if ((IsBitSet (PIND, 2)) == (IsBitClr (PIND, 4))) IencB0--;
// vgl. ISR(INT0_vect)
else IencB0++; // vgl. ISR(INT0_vect)
//
} // Ende ISR(PCINT2_vect)
// ================================================== =========================== =

. . . . Code gestrichen

// ================================================== =========================== =
// ===== ENDE Subroutinen ================================================= =
// ================================================== =========================== =

Der Wert tmrE0 enthält die Anzahl der Zeiteinheiten für die Drehzahlmessung - wird von einem Timer geliefert.

Bucki
03.07.2017, 09:21
Hallo oberallgeier!

Laut Datenblatt wird eigentlich nichts weiter verlangt, zur Sicherheit wurden aber Pull Down Widerstände verwendet. Einsehbar ist dieses hier: Daten Magnetgeber (http://www.dunkermotoren.de/produkte/bremsen-und-geber/gebersysteme/detail/8871004541/), dann im rechten Bereich unter Downloads "Gesamtüberblick" zu erreichen.

Und sonst mache ich es ja ähnlich wie du, nur dass ich meine Regelung nicht andauernd unterbrechen möchte, nur um eine einzige Flanke zu messen. Deswegen möchte ich für den Roboter als Hauptprogrammstruktur: Init --> Joystick einlesen --> aktuelle Drehzahl einlesen --> Regeln --> Beginne wieder bei Joystick einlesen. Deßhalb das mit der vorgegebenen Messdauer ( Die auch recht hoch ist, aber dies liegt wie gesagt an der geringen Geberauflösung ).
Am Oszi kommt alles wunderbar an und die Zeiten stimmen perfekt mit der angegebenen Leerlaufdrehzahl des Motors überein.
Nur eben der UART gibt Quark aus. Er scheint irgendwie nach einer Ausgabe hängen zu bleiben oder diese nichtmal richtig tätigen zu können ( siehe einmalige Ausgabe von Endekennung '\0'). :confused:

Grüße
Bucki

oberallgeier
03.07.2017, 10:10
.. Laut Datenblatt wird eigentlich nichts weiter verlangt .. Am Oszi kommt alles wunderbar an und die Zeiten stimmen ..Gut, oder - sorry, dass ich was anderes möglich gehalten habe ;-)


.. Nur eben der UART gibt Quark aus. Er scheint irgendwie nach einer Ausgabe hängen zu bleiben oder diese nichtmal richtig tätigen zu können ..DIES ist nur eine sehr persönliche Meinung: HTerm verwende ich so gut wie garnicht (nur für EINE Spezialaufgabe) - ich finde das Ding s..mässig schlecht. Sorry. Ich verwende das Teminal von br@y - die Version v1.9b-20040714 (http://www.smileymicros.com/download/term20040714.zip?&MMN_position=42:42).

Schau mal im Datenblatt des Controllers (http://www.atmel.com/Images/Atmel-42733-8-bit-AVR-Microcontroller-ATmega48A-88A-168A_Datasheet.pdf) nach - Seite 85/86. Die Interrupts zu den UART-Funktionen sind in der Interrupttabelle total hinten priorisiert. Da könn(t)en die - hoch priorisierten - externen oder PinChange-Interrupts prächtig dreinpfuschen ?? fürchtete ich manchmal. Vielleicht probiers mal vor/hinter der UART-Ausgabe mit cli();/sei(); - dafür aber übernehm ich nun keine Garantie :-/

Bucki
03.07.2017, 10:19
Okay, danke dir, werde ich mal versuchen! O:)

Naja bisher hat es seine Dienste gut vollzogen, habe das ganze Prozedere schon erfolgreich für das Joystick einlesen und die Ultraschallsensor-Auswertung verwenden können, aber bei beiden hatte ich noch keine Interrupts verwendet bzw. global extra diese gesperrt. Wenn ich es recht überlege könnte es wirklich an sowas Mimligen hapern. #-o
Melde mich diesbezüglich nochmal! ( Kanns aber vielleicht erst morgen testen )

Grüße
Bucki

RoboHolIC
03.07.2017, 22:19
Die Interrupts zu den UART-Funktionen sind in der Interrupttabelle total hinten priorisiert. Da könn(t)en die - hoch priorisierten - externen oder PinChange-Interrupts prächtig dreinpfuschen ??
@Bucki:
Nicht wundern falls cli();/sei(); keinen Erfolg bringt. Interrupts dürften die UART-Hardware gar nicht stören können. Die Priorisierung betrifft nach meinem Verständnis nur das Handling der Sende- und Empfangsregister bzw. deren ISRs. Jedes neue Zeichen wird doch anhand des Start-Bits erkannt und vom UART ganz stur rausgetickert. Könnt noch sein, dass einzelne Zeichen gar nicht gesendet sondern schlicht vom nachfolgenden Zeichstrom überrollt werden.