PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 100 kHz-Clock mit ATMega (16MHz) noch verarbeitbar?



Jaecko
28.06.2008, 16:17
Hi.

Hab hier nen ATMega2560 auf 16MHz.
Ich versuche gerade, Daten zu empfangen und Auszuwerten, die auf 3 Pins ankommen und mit 100 kHz auf einem Interrupt-Pin getaktet werden (digitaler Joystick)

Rechnerisch heisst das ja, dass für jeden Interrupt dann nur 160 Zyklen zur Verfügung stehen.

Die Tatsache, dass statt der erwarteten 48 Datenbits nur ca 20 ankommen, lässt mich vermuten, dass die 160 Zyklen aber nicht zu reichen scheinen.

Hier der Code der ISR vom Interrupt 3; jeweils 3 Datenbits (Triplett) an PINA1-3 werden eingelesen, an js_inbits angehängt und das ganze dann 3 Bit nach links verschoben.



SIGNAL (SIG_INTERRUPT3)
{
js_inbits |= ((PINA & 0x0E) >> 1);
js_inbits = (js_inbits << 3);
js_bitcount++;
}


Gibts da noch irgendwelche Tricks oder reichen die 16MHz einfach nicht?

mfg

fhs
28.06.2008, 17:02
Hallo,

in Abhängigkeit von der Anzahl anderer Interrupts sollte das schon funktionieren! Wie sind denn Deine Variablen deklariert? Bei js_inbits (ich nehme an, diese Variable ist global und volatile -- aber wieviele Bits breit?) multiplizierst Du jeweils mit 8, diese Variable wird also schnell überlaufen; oder setzt Du sie in einem anderen Teil des Programs zurück, wenn sich js_bitcount verändert hat? Vielleicht kannst Du auch den Rest des Codes mal posten? Erkennst Du an js_bitcount, dass Du nicht genügend Daten erhältst?

Gruß

Fred

Jaecko
28.06.2008, 17:30
Naja Rest des Codes wird schwierig, da es ausserhalb nur noch Debug-Funktionen gibt; also Daten per UART ein/ausgeben, Timer etc.

Die js_inbits ist zwar global, aber nicht volatile. Typ ist ui64, also unsigned long long; Platz für 64 Bits; da nur 48 erwartet werden, würde mit einem Datensatz sowieso nichts überlaufen, v.a. da sowieso nicht alles ankommt.

Als andere Interrupts gibts nur noch den Timer1 im Sekundentakt.

Besserwessi
28.06.2008, 17:47
Bei 64 bit Zahlen ist das shiften relativ aufwendig. Das dürft der langsamste teil sein. Mit dem Hardware multiplizeirer könnte da sogar ein einfaches *8 schneller sein, je nachdem wie gut der Compilter ist. Besser wäre es natürlich das shiften der ganzen daten ganz zu vermeiden. Das würde z.B. gehen indem man jeweils nur 6 Bits je Byte nutzt und dann einen Pointer auf das gerade aktuelle Byte nimmt. Das würde dann auch nur 8 bytes für die 48 datenbits brauchen.

Zur not müßte man die ISR routine in Asseembler schreiben, dann sollte es wohl noch reichen, auch mit dem ganzen Geschiebe. So lag ist die ISR ja bisher noch nicht.

Auch wenn der/die Anderen Interrupts nur selten vorkommen kann ein Aufruf einern langsamen Routine alles aufhalten. Da muß man mit worst case Zeiten rechnen.

fhs
28.06.2008, 17:59
Hi,


Die js_inbits ist zwar global, aber nicht volatile. Typ ist ui64, also unsigned long long; Platz für 64 Bits; ...

OK, Du setzt also das ganze Datenwort schon in der ISR zusammen. Das sehe ich wie Besserwessi: das Schieben durch 8 Bytes ist schon ziemlich zeitaufwendig, mit Assembler könnte es gehen.

Du verwendest js_inbits auch innerhalb Deiner "main" oder innerhalb von Funktionen? Dann würde ich js_inbits auf jeden Fall volatile machen!

Welchen Compiler benutzt Du? Hast Du Dir schon mal den entstandenen Assembler-Code angesehen und verschiedene Optimierungsstufen versucht?

Gruß

Fred

Jaecko
29.06.2008, 15:47
Also ich habs mal mit der Möglichkeit versucht, die emfpangenen 3 Bit in der ISR einfach in ein Byte-Array zu werfen; in jeder ISR wird der Index um 1 erhöht. Da reicht die Zeit dann.
Die Auswertung erfolgt dann, wenn alle Daten vorhanden sind.

Compiler wäre AVR Studio + WinAVR.

Noch ne Frage: Da ich die empfangenen Daten der Übersichtlichkeit halber später noch in nem Struct ablegen will:
Gibts irgend ne Möglichkeit, Variablen mit ner bestimmten Anzahl Bits zu casten, die jetzt kein ganzzahliges Vielfaches von 8 sind?

Also so, dass man z.B. einem Struct-Element, das nur 2 Bit lang ist, die ersten beiden Bits eines Bytes zuweisen kann, ohne dass durch die restlichen 6 Bit andere Elemente des Structs überschrieben werden?
So im Stil von "wordvariable = (ui8_t) bytevariable", nur eben
"struct.element = (ui2_t) bytevariable;"

Wie müsste da der Typedef für ui2_t aussehen?

fhs
30.06.2008, 07:55
Hallo,

ich hoffe, ich habe Deine Frage richtig verstanden. Hier ein Code-Beispiel unter Verwendung von Bitfields:


typedef struct {
union{
uint8_t byte;
unsigned lower2bits: 2;
};
} w2b;

w2b mybyte;
mybyte.byte=128; //0x80
mybyte.lower2bits=7; //0x07, aber nur die 2 LSBits (0x03) werden verwendet!


Du hast hier also eine Union aus 8 Bits (mybyte.byte) und den 2 LSBits (mybyte.lower2bits). Im Beispiel setze ich mybyte.byte zunächst auf 128, dann setze ich die niedrigsten 2 Bits auf 0x7 -- da aber nur Bits 0 und 1 verändert werden sollen, verschwindet das Bit 2. Im Endergebnis erhält man für mybyte.byte also 0x83=131 .

Gruß

Fred

Jaecko
30.06.2008, 08:05
Thx, genau so war die Frage.
Da ich bei dem bisherigen Versuch (nur ne einfache Struct) das Problem hatte, dass dort eben im Speicher nachfolgende Elemente überschrieben wurden.

fhs
30.06.2008, 08:11
Hi,

ein paar Vorsichtshinweise muss ich noch anhängen: Wenn Du nur mit einem Compiler arbeitest und weißt, wie der mit Bitfields umgeht, ist alles kein Problem. Beim Wechsel zu einem anderen Compiler und einem anderen Prozessor (anderer Hersteller), hast Du alle Kompatibilität verloren.

Zudem ist es durchaus möglich, dass der Compiler sehr ineffizienten (=langsamen!) Code für die Bitfields generiert.

Übrigens: Am besten Bitfields immer "unsigned" machen!

Viel Erfolg mit Deinem Projekt!

Fred

Jaecko
30.06.2008, 08:41
Thxle nochmal für die Hinweise.
Das mit dem Code hab ich gerade mal ausprobiert.
Also Versuch 1: Bitfelder nur so gross, wie benötigt; Versuch 2: Bitfelder aufgerundet auf ganze Bytes/Word.
Die Codegrösse beim 2. Versuch ist um 1092 Bytes kleiner als beim ersten.

Hab den bisherigen Joystick-Teil auch mal hier als Code mit angehängt; ist für nen Microsoft Sidewinder Force Feedback Pro; evtl kanns ja jemand brauchen um nen Roboter anzusteuern.
Was später noch kommt, ist auch ne Rückmeldung per Force Feedback. Elektrisch kein Problem; lediglich ein Steuerprotokoll fehlt mir noch.

mit JS_SWFFP_Init() einfach 1x initialisieren; JS_SWFFP_GetData() fragt den Joystick ab und befördert die ausgewerteten Daten in die Struct js_ffb.

joystick.c:


#include "joystick.h"

void JS_SWFFP_GetData(void)
{
js_rectripindex = 0; // Reset triples index
JS_SWFFP_Trigger(); // Trigger joystick measurement
while(js_rectripindex < 17); // Wait until data has been received

js_ffb.buttons = 0; // Reset variables
js_ffb.axis0 = 0;
js_ffb.axis1 = 0;
js_ffb.axis2 = 0;
js_ffb.axis3 = 0;
js_ffb.hat = 0;

js_ffb.buttons |= ((~js_rectriplets[0] & 0x0E) >> 1); // mask out buttons
js_ffb.buttons |= ((~js_rectriplets[1] & 0x0E) << 2);
js_ffb.buttons |= ((~js_rectriplets[2] & 0x0E) << 5);

js_ffb.axis0 |= ((js_rectriplets[3] & 0x0E) >> 1); // mask out axis 0 (X)
js_ffb.axis0 |= ((js_rectriplets[4] & 0x0E) << 2);
js_ffb.axis0 |= ((js_rectriplets[5] & 0x0E) << 5);
js_ffb.axis0 |= ((js_rectriplets[6] & 0x02) << 8);

js_ffb.axis1 |= ((js_rectriplets[6] & 0x0C) >> 2); // mask out axis 1 (Y)
js_ffb.axis1 |= ((js_rectriplets[7] & 0x0E) << 1);
js_ffb.axis1 |= ((js_rectriplets[8] & 0x0E) << 4);
js_ffb.axis1 |= ((js_rectriplets[9] & 0x06) << 7);

js_ffb.axis2 |= ((js_rectriplets[9] & 0x08) >> 3); // mask out axis 2 (Throttle)
js_ffb.axis2 |= ((js_rectriplets[10] & 0x0E) >> 0);
js_ffb.axis2 |= ((js_rectriplets[11] & 0x0E) << 3);
js_ffb.axis2 = 127 - js_ffb.axis2; // invert axis 2 (Throttle)

js_ffb.axis3 |= ((js_rectriplets[12] & 0x0E) >> 1); // mask out axis 3 (Rudder)
js_ffb.axis3 |= ((js_rectriplets[13] & 0x0E) << 2);

js_ffb.hat |= ((js_rectriplets[14] & 0x0E) >> 1); // mask out hat
js_ffb.hat |= ((js_rectriplets[15] & 0x02) << 2);
}

void JS_SWFFP_Init(void)
{
EIMSK |= (1 << INT3); // Enable INT3 (Pin D.3)
EICRA |= ((1 << ISC31) | (1 << ISC30)); // Trigger: Rising edge
js_rectripindex = 0;
}

void JS_SWFFP_DeInit(void)
{
EIMSK &= ~(1 << INT3); // Disable INT3 (Pin D.3)
EICRA &= ~((1 << ISC31) | (1 << ISC30)); // Trigger: Disabled
js_rectripindex = 0;
}


void JS_SWFFP_Trigger(void)
{
// Trigger measurement by generating a short pulse
JS_PORT_TRIGGER |= (1 << JS_PIN_TRIGGER);
_delay_us(50);
JS_PORT_TRIGGER &= ~(1 << JS_PIN_TRIGGER);
}


SIGNAL (SIG_INTERRUPT3)
{
// Get triplets
js_rectriplets[js_rectripindex] = (PINA & 0x0E);
js_rectripindex++;
}


joystick.h:


#ifndef _JOYSTICK_H_
#define _JOYSTICK_H_

#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <util/delay.h>
#include "glob_type.h"

/* Ports & Pins, where the data input lines are connected */
#define JS_PORT_BIT1 PINA
#define JS_PORT_BIT2 PINA
#define JS_PORT_BIT3 PINA
#define JS_PIN_BIT1 1 // Button 1
#define JS_PIN_BIT2 2 // Button 2
#define JS_PIN_BIT3 3 // Button 3

/* Measurement trigger output */
#define JS_PORT_TRIGGER PORTA
#define JS_PIN_TRIGGER 4

/* MIDI Tx; Not used yet; for implementing Force Feedback functionalities later via UART*/
#define JS_PORT_FFB PORTA
#define JS_PIN_FFB 5

volatile ui8_t js_rectriplets[16]; // Array for triplet reception
volatile ui8_t js_rectripindex; // Triplet array index

void JS_SWFFP_GetData(void);
void JS_SWFFP_Trigger(void);
void JS_SWFFP_DeInit(void);
void JS_SWFFP_Init(void);


/*
Microsoft Sidewinder Force Feedback Pro
Triplets: 16 (= Total 48 bits)
[00 - 08]: Buttons ( 9 bits)
[09 - 18]: Axis 0 (X) (10 bits)
[19 - 28]: Axis 1 (Y) (10 bits)
[29 - 35]: Axis 2 (Throttle) ( 7 bits)
[36 - 41]: Axis 3 (Rudder) ( 6 bits)
[42 - 45]: Hat ( 4 bits)
[46]: Always 1 (ignored)
[47]: Parity (ignored)
*/


/*
No bit 'cutting' used in this struct; using a whole byte/word instead of needed number of bits
saves about 1k flash memory.
*/
typedef struct {
ui16_t buttons;
ui16_t axis0;
ui16_t axis1;
ui8_t axis2;
ui8_t axis3;
ui8_t hat;
} stc_js_swffp;


/*

typedef struct {
union {
elemente typ : bit;
};
} stc_name;

*/


volatile stc_js_swffp js_ffb;

#endif


glob_types.h


#ifndef _GLOB_TYPE_H_
#define _GLOB_TYPE_H_

/* This header file contains global types */

// Standard
typedef signed char i8_t;
typedef unsigned char ui8_t;
typedef short i16_t;
typedef unsigned short ui16_t;
typedef long i32_t;
typedef unsigned long ui32_t;
typedef long long i64_t;
typedef unsigned long long ui64_t;

// Precise time
typedef struct{
ui8_t hour;
ui8_t min;
ui8_t sec;
ui8_t msec;
} stc_timeprec;

// Standard time
typedef struct{
ui8_t hour;
ui8_t min;
ui8_t sec;
} stc_timestd;

// Standard date
typedef struct{
ui16_t year;
ui8_t month;
ui8_t day;
} stc_date;

// Timestamp
typedef struct{
stc_date date;
stc_timeprec time;
} stc_timestamp;

// Item for Menu State Machine
typedef struct{
ui16_t s_current;
ui16_t s_keyfunc;
ui16_t s_next;
} stc_menuitem;


#endif