PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Winavr - Direktzuweisung ?



lundi
20.09.2005, 15:27
Hallo,
bin ziemlich neu bei den avrs und winavr gelandet, hab bis jetzt nur 8051er über wrkit bzw. keil verwendet.
warum kann ich in dem winavr eigentlich die bits nicht dirket einen wert zuweisen wie es in anderen compilern geht ?
z.B.: PORTC.2=1;

genauso meckert der compiler bei anweisung wie: PORTB &= 0x0F;

mach ich irgendwas falsch oder habe ich nur eine alte version des compilers?

Kjion
20.09.2005, 16:54
warum kann ich in dem winavr eigentlich die bits nicht dirket einen wert zuweisen wie es in anderen compilern geht ?
z.B.: PORTC.2=1;
Weil es das in C nicht gibt. PORTC |= (1<<2); wäre die richtige Schreibweise ...


genauso meckert der compiler bei anweisung wie: PORTB &= 0x0F;
Da darf der Compiler eigentlich nicht meckern. Hast du am Anfang die avr/io.h eingebunden ??

#include <avr/io.h>
Ansonsten weiß der Compiler nichts mit den Registerbezeichnungen anzufangen.


(...) oder habe ich nur eine alte version des compilers?
Das ist schwierig zu sagen ohne zu wissen welche Version du verwendest :-)

Version 20050214 ist die aktelle ( siehe http://sourceforge.net/projects/winavr/ )

MfG Kjion

lundi
21.09.2005, 07:20
Vielen Dank hatte wirklich eine alte Version.
die avr/io.h habe ich eingebunden aber ihm gefällts trotzdem nicht.



kijon hat folgendes geschrieben:


lundi hat folgendes geschrieben::
warum kann ich in dem winavr eigentlich die bits nicht dirket einen wert zuweisen wie es in anderen compilern geht ?
z.B.: PORTC.2=1;

Weil es das in C nicht gibt. PORTC |= (1<<2); wäre die richtige Schreibweise ...


warum funktioniert die dirktanweisung( PORTC.2=1 ) dann im wrkit bzw. keil das sind doch auch c-compiler?

m.a.r.v.i.n
21.09.2005, 08:06
Hallo lundi,

Um die Eigenheiten von winavr besser zu verstehen, empfielt es sich auf alle Fälle mal das AVR-GCC Tutorial durchzuarbeiten:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

Gruß Peter

Kjion
21.09.2005, 11:32
warum funktioniert die dirktanweisung( PORTC.2=1 ) dann im wrkit bzw. keil das sind doch auch c-compiler?
Das sind dann Compilerspezifische Erweiterungen. Im C Standart gibt es diese Operationen nicht.

MfG Kjion

SprinterSB
21.09.2005, 16:54
Das Problem mit den Ports ist recht nervig. Vor allem dann, wenn man mal einen Port umdefiniert und dann alle Quellen durchsehen muss.
Ich habe mir mit Macros geholfen und das funktioniert ganz gut, wenn man optimiert (ab -O ).
Als erstes definiere ich Makros, die eine Konstante 0, 1, ... etc auf einen Port abbilden. Diese Makros sind Target-unabhängig.
avr-port-macros.h


#ifndef _AVR_PORT_MACROS_H_
#define _AVR_PORT_MACROS_H_

#ifndef PORT_ID
#define PORT_ID(n) \
(n ## _ID << 3)
#endif /* PORT_ID */

#ifdef USE_SERPA

extern unsigned char serpa[];
extern unsigned char serpa_read[];
extern const unsigned char SERPA_NUM;
//#define SERPA_NUM 4

//unsigned char serpa[SERPA_NUM];

#define IS_SERPA_PORT(pinid) \
((SERPA_ID <= (pinid) >> 3) /* && (SERPA_ID+SERPA_NUM > (pinid) >> 3)*/)

#define N_TO_SERPA(pinid) \
serpa[((pinid) >> 3)-SERPA_ID]

#define N_TO_SERPAIN(pinid) \
serpa_read[0]

#define IF_SERPA_OP_ELSE(pinid,op) \
if (IS_SERPA_PORT (pinid))\
N_TO_SERPA(pinid) op ID_TO_BIT(pinid); \
else

#else /* USE_SERPA */

//#define SERPA_NUM 0
#define IF_SERPA_OP_ELSE(pinid,op)
#define N_TO_SERPA(pinid) 0
#define N_TO_SERPAIN(pinid) 0

#endif /* USE_SERPA */

#define IS_HARD_PORT(pinid) \
((pinid >> 3) < SERPAIN_ID)

/** Set a PORT bit to 1 */
#define SET(pinid) \
do { \
if ((pinid) >> 3 == SERPAIN_ID); else \
IF_SERPA_OP_ELSE(pinid, |=) \
N_TO_PORT (pinid) |= ID_TO_BIT(pinid); \
} while (0)

/** Set a PORT bit to 0 */
#define CLR(pinid) \
do { \
if ((pinid) >> 3 == SERPAIN_ID); else \
IF_SERPA_OP_ELSE(pinid, &= ~) \
N_TO_PORT (pinid) &= ~ID_TO_BIT(pinid); \
} while (0)

/** Toggle a PORT bit.
* Note: The generated code will NOT be atomic!
*/
#define TOGGLE(pinid) \
do { \
if ((pinid) >> 3 == SERPAIN_ID); else \
IF_SERPA_OP_ELSE(pinid, ^= ) \
N_TO_PORT (pinid) ^= ID_TO_BIT(pinid); \
} while (0)

/** Set DDR to 1 */
#define MAKE_OUT(pinid) \
do { \
if (IS_HARD_PORT(pinid)) \
N_TO_DDR (pinid) |= ID_TO_BIT(pinid); \
} while (0)

/** Set DDR to 0 */
#define MAKE_IN(pinid) \
do { \
if (IS_HARD_PORT(pinid)) \
N_TO_DDR (pinid) &= ~ID_TO_BIT(pinid); \
} while (0)

/** Is PIN == 1 ? */
#define IS_SET(pinid) \
(IS_HARD_PORT(pinid) \
? (N_TO_PIN (pinid) & ID_TO_BIT(pinid)) \
: (unsigned char) ((unsigned char) N_TO_SERPAIN(pinid) & (unsigned char) ID_TO_BIT(pinid)))

/** Is PIN == 0 ? */
#define IS_CLEAR(pinid) \
(!IS_SET(pinid))

/** Is PORT == 1 ? */
#define IS_PORT(pinid) (N_TO_PORT (pinid) & ID_TO_BIT(pinid))

/*
* IDs for the various PORTs of our AVRs.
* If you want to make this work for targets with
* even more ports (like ATMega128 etc) just add these
* ports in the following macro definitions.
* Using #error would require making the macros target dependent.
*/
enum {
PORTA_ID,
PORTB_ID,
PORTC_ID,
PORTD_ID,

SERPAIN_ID,
SERPA_ID
};

#define pDDRX(pinid) ({asm volatile (".bad_ddr_id_" # pinid);(unsigned char*) 0;})
#define pPORTX(pinid) ({asm volatile (".bad_port_id_" # pinid);(unsigned char*) 0;})
#define pPINX(pinid) ({asm volatile (".bad_pin_id_" # pinid);(unsigned char*) 0;})

/*
* #define addresses of PORTs or an assembler error if
* a non-existing PORT is refered.
* I found no way to tell this error by the (pre)compiler.
* Note that #error does not work as expected here.
*/
#if defined(PORTA)
#define pPORTA &PORTA
#else
#define pPORTA ({asm volatile (".target_has_no_porta");(unsigned char*) 0;})
#endif

#if defined(PORTB)
#define pPORTB &PORTB
#else
#define pPORTB ({asm volatile (".target_has_no_portb");(unsigned char*) 0;})
#endif

#if defined(PORTC)
#define pPORTC &PORTC
#else
#define pPORTC ({asm volatile (".target_has_no_portc");(unsigned char*) 0;})
#endif

#if defined(PORTD)
#define pPORTD &PORTD
#else
#define pPORTD ({asm volatile (".target_has_no_portd");(unsigned char*) 0;})
#endif

/*
* #define addresses of DDRs or an assembler error if
* a non-existing DDR is refered.
*/
#if defined(DDRA)
#define pDDRA &DDRA
#else
#define pDDRA ({asm volatile (".target_has_no_ddra");(unsigned char*) 0;})
#endif

#if defined(DDRB)
#define pDDRB &DDRB
#else
#define pDDRB ({asm volatile (".target_has_no_ddrb");(unsigned char*) 0;})
#endif

#if defined(DDRC)
#define pDDRC &DDRC
#else
#define pDDRC ({asm volatile (".target_has_no_ddrc");(unsigned char*) 0;})
#endif

#if defined(DDRD)
#define pDDRD &DDRD
#else
#define pDDRD ({asm volatile (".target_has_no_ddrd");(unsigned char*) 0;})
#endif

/*
* #define addresses of PINs or an assembler error if
* a non-existing PIN is refered.
*/
#if defined(PINA)
#define pPINA &PINA
#else
#define pPINA ({asm volatile (".target_has_no_pina");(unsigned char*) 0;})
#endif

#if defined(PINB)
#define pPINB &PINB
#else
#define pPINB ({asm volatile (".target_has_no_pinb");(unsigned char*) 0;})
#endif

#if defined(PINC)
#define pPINC &PINC
#else
#define pPINC ({asm volatile (".target_has_no_pinc");(unsigned char*) 0;})
#endif

#if defined(PIND)
#define pPIND &PIND
#else
#define pPIND ({asm volatile (".target_has_no_pind");(unsigned char*) 0;})
#endif


#define N_TO_pPORT(pinid) \
( \
(PORTA_ID == (pinid) >> 3) ? pPORTA : \
(PORTB_ID == (pinid) >> 3) ? pPORTB : \
(PORTC_ID == (pinid) >> 3) ? pPORTC : \
(PORTD_ID == (pinid) >> 3) ? pPORTD : \
pPORTX(pinid) \
)

#define N_TO_pDDR(pinid) \
( \
(PORTA_ID == (pinid) >> 3) ? pDDRA : \
(PORTB_ID == (pinid) >> 3) ? pDDRB : \
(PORTC_ID == (pinid) >> 3) ? pDDRC : \
(PORTD_ID == (pinid) >> 3) ? pDDRD : \
pDDRX(pinid) \
)

#define N_TO_pPIN(pinid) \
( \
(PORTA_ID == (pinid) >> 3) ? pPINA : \
(PORTB_ID == (pinid) >> 3) ? pPINB : \
(PORTC_ID == (pinid) >> 3) ? pPINC : \
(PORTD_ID == (pinid) >> 3) ? pPIND : \
pPINX(pinid) \
)

#define N_TO_PORT(pinid) (*(N_TO_pPORT(pinid)))
#define N_TO_DDR(pinid) (*(N_TO_pDDR(pinid)))
#define N_TO_PIN(pinid) (*(N_TO_pPIN(pinid)))

#define CHECK_PORT_MACRO(port) \
do { \
if (&PORT ## port != N_TO_pPORT (PIN_ID (port,0))) asm volatile (".error_in_N_TO_pPORT_" #port); \
if (&DDR ## port != N_TO_pDDR (PIN_ID (port,0))) asm volatile (".error_in_N_TO_pDDR_" #port); \
if (&PIN ## port != N_TO_pPIN (PIN_ID (port,0))) asm volatile (".error_in_N_TO_pPIN_" #port); \
} while (0)

#endif /* _AVR_PORT_MACROS_H_ */

Im zweiten File werden je nach verwendetem Target die vorhandenen Ports auf Konstanten abgebildet:
avr-port-enum.h


#ifndef _AVR_PORT_ENUM_H_
#define _AVR_PORT_ENUM_H_

/************************************************** *********/

#ifndef ID_TO_BIT
#define ID_TO_BIT(pinid) \
(1<<((pinid)&7))
#endif /* ID_TO_BIT */

#define PIN_ID(c,n) \
((PORT ## c ## _ID << 3) | (n))

#define ENUM_PIN(letter,pin) \
PORT ## letter ## _ ## pin = PIN_ID(letter,pin)

#define ENUM_PORT(p) \
ENUM_PIN (p,0), \
ENUM_PIN (p,1), \
ENUM_PIN (p,2), \
ENUM_PIN (p,3), \
ENUM_PIN (p,4), \
ENUM_PIN (p,5), \
ENUM_PIN (p,6), \
ENUM_PIN (p,7)

/*
* Enumerate all ports of respective MCU,
* giving us the desired values like PORTD_1 or PORTB_4, e.g.
*/

#if defined(__AVR_AT90S2313__)
enum {
ENUM_PORT (B),

ENUM_PIN (D,0),
ENUM_PIN (D,1),
ENUM_PIN (D,2),
ENUM_PIN (D,3),
ENUM_PIN (D,4),
ENUM_PIN (D,5),
ENUM_PIN (D,6),

ENUM_PIN_LAST
};
#elif defined(__AVR_ATmega8__)
enum {
ENUM_PORT (B),

ENUM_PIN (C,0),
ENUM_PIN (C,1),
ENUM_PIN (C,2),
ENUM_PIN (C,3),
ENUM_PIN (C,4),
ENUM_PIN (C,5),
ENUM_PIN (C,6),

ENUM_PORT (D),

ENUM_PIN_LAST
};
#elif defined(__AVR_AT90S2333__) || defined(__AVR_AT90S4433__)
enum {
ENUM_PIN (B,0),
ENUM_PIN (B,1),
ENUM_PIN (B,2),
ENUM_PIN (B,3),
ENUM_PIN (B,4),
ENUM_PIN (B,5),

ENUM_PIN (C,0),
ENUM_PIN (C,1),
ENUM_PIN (C,2),
ENUM_PIN (C,3),
ENUM_PIN (C,4),
ENUM_PIN (C,5),

ENUM_PORT (D),

ENUM_PIN_LAST
};
#elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || \
defined(__AVR_AT90S4434__) || defined(__AVR_AT90S8535__) || \
defined(__AVR_ATmega163__)
enum {
ENUM_PORT (A),
ENUM_PORT (B),
ENUM_PORT (C),
ENUM_PORT (D),

ENUM_PIN_LAST
};
#else
#error Please define available ports for MCU
#endif

#if 0
/* Code snippet from avr-gcc source */
static const struct mcu_type_s avr_mcu_types[] = {
/* Classic, <= 8K. */
{ "avr2", 2, NULL },
{ "at90s2313", 2, "__AVR_AT90S2313__" },
{ "at90s2323", 2, "__AVR_AT90S2323__" },
{ "at90s2333", 2, "__AVR_AT90S2333__" },
{ "at90s2343", 2, "__AVR_AT90S2343__" },
{ "attiny22", 2, "__AVR_ATtiny22__" },
{ "attiny26", 2, "__AVR_ATtiny26__" },
{ "at90s4414", 2, "__AVR_AT90S4414__" },
{ "at90s4433", 2, "__AVR_AT90S4433__" },
{ "at90s4434", 2, "__AVR_AT90S4434__" },
{ "at90s8515", 2, "__AVR_AT90S8515__" },
{ "at90c8534", 2, "__AVR_AT90C8534__" },
{ "at90s8535", 2, "__AVR_AT90S8535__" },
{ "at86rf401", 2, "__AVR_AT86RF401__" },
/* Classic, > 8K. */
{ "avr3", 3, NULL },
{ "atmega103", 3, "__AVR_ATmega103__" },
{ "atmega603", 3, "__AVR_ATmega603__" },
{ "at43usb320", 3, "__AVR_AT43USB320__" },
{ "at43usb355", 3, "__AVR_AT43USB355__" },
{ "at76c711", 3, "__AVR_AT76C711__" },
/* Enhanced, <= 8K. */
{ "avr4", 4, NULL },
{ "atmega8", 4, "__AVR_ATmega8__" },
{ "atmega8515", 4, "__AVR_ATmega8515__" },
{ "atmega8535", 4, "__AVR_ATmega8535__" },
/* Enhanced, > 8K. */
{ "avr5", 5, NULL },
{ "atmega16", 5, "__AVR_ATmega16__" },
{ "atmega161", 5, "__AVR_ATmega161__" },
{ "atmega162", 5, "__AVR_ATmega162__" },
{ "atmega163", 5, "__AVR_ATmega163__" },
{ "atmega169", 5, "__AVR_ATmega169__" },
{ "atmega32", 5, "__AVR_ATmega32__" },
{ "atmega323", 5, "__AVR_ATmega323__" },
{ "atmega64", 5, "__AVR_ATmega64__" },
{ "atmega128", 5, "__AVR_ATmega128__" },
{ "at94k", 5, "__AVR_AT94K__" },
/* Assembler only. */
{ "avr1", 1, NULL },
{ "at90s1200", 1, "__AVR_AT90S1200__" },
{ "attiny11", 1, "__AVR_ATtiny11__" },
{ "attiny12", 1, "__AVR_ATtiny12__" },
{ "attiny15", 1, "__AVR_ATtiny15__" },
{ "attiny28", 1, "__AVR_ATtiny28__" },
{ NULL, 0, NULL }
};
#endif /* 0 */

#endif /* _AVR_PORT_ENUM_H_ */


Das sieht alles recht kompliziert aus, aber das kann man wie eine Black Box benutzen und muss es nicht verstehen. Diese beiden Dateien verwende ich in allen Projekten. Sie liegen in einem Verzeichnis ausserhalb der Projekte und den Pfad gebe ich gcc mit dem -I Schalter mit. Etwa:
avr-gcc ... -Ic:/avr-projekte/include ...
Die Dateien in jedes Projekt zu kopieren empiehlt sich nicht, weil man dadurch 1000 Kopien der selben Dateien hat.

Verwendung:
In jedem Projekt hab ich eine Datei ports.h, die etwa so aussehen könnte


#ifndef _PORTS_H_
#define _PORTS_H_

#include <avr/io.h>
// Make sure that gcc can find avr-port-xxx.h
// Use the -I option to tell gcc where to find these files.
// avr-gcc ... -Ic:/foo ... (windows)
// avr-gcc ... -I/usr/local/foo ...(linux)
#include <avr-port-macros.h>
#include <avr-port-enum.h>

enum
{
PORT_LED = PORTD_2,

PORT_SER = PORTD_4,
PORT_SCK = PORTD_5,
PORT_RCK = PORTD_6,

PORT_TAST = PORTB_3,

#ifdef USE_DCF
PORT_DCF = PORTD_0,
#endif /* USE_DCF */

// sentinel
PORT_NIL
};

#endif /* _PORTS_H_ */


Hier ein Beispiel zur Verwendung:


#include <avr/io.h>
#include "ports.h"

void main() // compiled with -ffreestanding
{
// PORT_LED ist Output
MAKE_OUT (PORT_LED);
// PORT_LED aus
CLR (PORT_LED);

// PORT_TAST Input mit Pullup
MAKE_IN (PORT_TAST);
SET (PORT_TAST);

if (!IS_SET (PORT_TAST))
CLR (PORT_LED);

if (IS_CLEAR (PORT_TAST))
TOGGLE (PORT_LED);

if (!IS_PORT (PORT_LED))
SET (PORT_LED);
}


Definierte Makros

SET(id)
Setzt PORTx auf 1.
Beispiel: SET (PORTD_0);
CLR(id)
Setzt PORTx auf 0.
Beispiel: CLR (PORTD_0);
TOGGLE(id)
Wechselt den Zustand von PORTx.
Beispiel: TOGGLE (PORTD_0);
Anmerkung: Hier werden mehrere asm-Instruktionen generiert, der erzeugte Code ist also nicht atomar.
MAKE_OUT(id)
Setzt DDRx auf 1.
Beispiel: MAKE_OUT (PORTD_0);
MAKE_IN(id)
Setzt DDRx auf 0.
Beispiel: MAKE_IN (PORTD_0);
IS_SET(id)
Testet PINx auf 1.
Beispiel: if (IS_SET (PORTD_0)) {...}
Beispiel: if (!IS_SET (PORTD_0)) {...}
IS_CLEAR(id)
Testet PINx auf 0.
Beispiel: if (IS_CLEAR (PORTD_0)) {...}
Beispiel: if (!IS_CLEAR (PORTD_0)) {...}
IS_PORT(id)
Testet PORTx auf 1.
Beispiel: if (IS_PORT (PORTD_0)) {...}
Anmerkung: wird man normalerweise nicht brauchen

Wie man sieht wird mittels PORTD_0 sowohl das passende DDR, PORT und PIN angesprochen! Schreibt man nicht PORTD_0 hin sondern
#define PORT_LED PORTD_0
oder
enum
{
...
PORT_LED = PORTD_0,
...
};
dann muss man nur an 1 Stelle im Code anpacken, um alle Zugriffe auf einen anderen Port zu lenken!

Durch die komplexen Makros wird erst mal recht viel C-Code erzeugt. Da alles Konstanten sind und die Ausdrücke zur Compile-Zeit ausgewertet werden können, kollabieren fast alle zu einer einzigen asm-Instruktion, so daß in *.s alles recht gut aussieht:

Assembler-Ausgabe des Beispiels


; GNU C version 3.4.1 (avr)
; compiled by GNU C version 3.3.1 (cygming special).
; GGC heuristics: --param ggc-min-expand=38 --param ggc-min-heapsize=16318
; options passed: -fpreprocessed -mmcu=atmega8 -auxbase -O1 -Wall
; -Winline -ffreestanding -fno-keep-inline-functions -fverbose-asm
; options enabled: -feliminate-unused-debug-types -fdefer-pop
; -fomit-frame-pointer -fthread-jumps -fpeephole -ffunction-cse
; -fkeep-static-consts -freg-struct-return -fgcse-lm -fgcse-sm -fgcse-las
; -floop-optimize -fif-conversion -fif-conversion2 -fsched-interblock
; -fsched-spec -fsched-stalled-insns -fsched-stalled-insns-dep
; -fbranch-count-reg -fcprop-registers -fcommon -fverbose-asm
; -fargument-alias -fmerge-constants -fzero-initialized-in-bss -fident
; -fguess-branch-probability -fmath-errno -ftrapping-math
; -minit-stack=__stack -mmcu=atmega8

.text
.global main
.type main, @function
main:
/* prologue: frame size=0 */
ldi r28,lo8(__stack - 0)
ldi r29,hi8(__stack - 0)
out __SP_H__,r29
out __SP_L__,r28
/* prologue end (size=4) */
sbi 49-0x20,2 ; , ; 15 *sbi [length = 1]
cbi 50-0x20,2 ; , ; 31 *cbi [length = 1]
cbi 55-0x20,3 ; , ; 42 *cbi [length = 1]
sbi 56-0x20,3 ; , ; 58 *sbi [length = 1]
sbis 54-0x20,3 ; , ; 68 *sbix_branch [length = 2]
cbi 50-0x20,2 ; , ; 82 *cbi [length = 1]
.L12:
sbic 54-0x20,3 ; , ; 93 *sbix_branch [length = 2]
rjmp .L16 ;
in r24,50-0x20 ; tmp74, ; 105 *movqi/4 [length = 1]
ldi r25,lo8(4) ; tmp75, ; 106 *movqi/2 [length = 1]
eor r24,r25 ; tmp73, tmp75 ; 107 xorqi3 [length = 1]
out 50-0x20,r24 ; , tmp73 ; 108 *movqi/3 [length = 1]
.L16:
sbis 50-0x20,2 ; , ; 119 *sbix_branch [length = 2]
sbi 50-0x20,2 ; , ; 133 *sbi [length = 1]
.L1:
/* epilogue: frame size=0 */
rjmp exit
/* epilogue end (size=1) */


Vielleicht findet jemand so ein Paket auch praktisch?
Würd mich über Rückmeldungen freuen.

Felix G
21.09.2005, 19:10
Das Problem mit den Ports ist recht nervig. Vor allem dann, wenn man mal einen Port umdefiniert und dann alle Quellen durchsehen muss.
Also das musst du aber mal genauer erklären, denn irgendwie sehe ich da kein Problem !?

Wenn mir bei Port A die Bezeichnung PORTA nicht gefällt,
und ich der Meinung bin daß EIERSALAT viel besser passen würde,
dann reicht doch ein einfaches #define EIERSALAT PORTA

klar, wenn ich schon im ganzen Projekt überall mit PORTA gearbeitet habe ists ein bischen nervig,
dann muss man halt ein paar Sekunden Zeit investieren und replace drüberjagen.


Dein Makro-Konstrukt finde ich ehrlichgesagt ein bischen übertrieben...
das ganze geht nämlich genauso gut auch ohne:

// Bit setzen:
Variable |= (1 << BitNr);

// Bit löschen:
Variable &= ~(1 << BitNr);

// Bit togglen:
Variable ^= (1 << BitNr);

// Bit gesetzt?
if( Variable & (1 << BitNr) )

// Bit nicht gesetzt?
if( !(Variable & (1 << BitNr)) )
kurz, einfach, schnell erlernt

Ich mache das so, und jeder den ich kenne macht das auch so...

Kjion
21.09.2005, 19:44
// Bit gesetzt?
if( Variable & (1 << BitNr) )

// Bit nicht gesetzt?
if( !(Variable & (1 << BitNr)) )

Das geht sogar noch schöner:

// Bit gesetzt?
if( bit_is_set(Variable, BitNr) )

// Bit nicht gesetzt?
if( bit_is_clear(Variable, BitNr) )

Ich sehe da jetzt auch eigentlich kein Problem ...

MfG Kjion

Felix G
21.09.2005, 19:57
naja, genau solche Sachen vermeide ich gerne...

denn die "Variable & (1 << BitNr)"-Variante ist reines Standard-C und funktioniert garantiert mit jedem beliebigen Compiler.

bit_is_set und bit_is_clear hingegen sind wieder irgendwelche Makros die irgendwo definiert sein müssen.

Kjion
22.09.2005, 09:05
bit_is_set und bit_is_clear hingegen sind wieder irgendwelche Makros die irgendwo definiert sein müssen.

Die Makros sind in der avr/sfr_defs.h definiert, werden also auf jeden Fall mit eingebunden wenn du avr/io.h einbindest.

Und wirklich kompiliziert sind sie auch nicht, ist eben nur ein schönere Beschreibung die man auf den ersten Blick versteht ( jedenfalls geht es mir so ):

#define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))

MfG Kjion

cavegn
22.09.2005, 09:45
jum, aber sie sind compiler-abhängig

wenn du mal einen nicht-gcc compiler benützt, verwendet dieser andere headerfiles, und da sind diese makros wahrscheinlich nicht definiert.

cu

chris

ps: das heisst natürlich nicht, dass das verwenden dieser makros 'böse' ist, bei einem anderen compiler wird man einfach die namen per search & replace änder müssen :-)

PicNick
22.09.2005, 12:07
Es kommt darauf an, ob du für den eigenen Bedarf Programme schnitzt oder öfters mit Fremdarbeiten / -Umgebungen beschäftigt bist. (--> Lesbarkeit)
Im letzteren Fall gewöhnt man sich solche (praktischen) Extras halt schnell ab. Mußt du fremde Programm lesen, bis du froh, wenn du nicht bei jedem zweiten Wort erst schauen mußt, was da dahinter steckt.
Ein "besser" / "schlechter" ist da immer Ansichtssache
(IMHO)

SprinterSB
22.09.2005, 13:55
Also das musst du aber mal genauer erklären, denn irgendwie sehe ich da kein Problem !?

Wenn mir bei Port A die Bezeichnung PORTA nicht gefällt,
und ich der Meinung bin daß EIERSALAT viel besser passen würde,
dann reicht doch ein einfaches #define EIERSALAT PORTA

klar, wenn ich schon im ganzen Projekt überall mit PORTA gearbeitet habe ists ein bischen nervig,
dann muss man halt ein paar Sekunden Zeit investieren und replace drüberjagen.
...und dabei alle anderen PORTA überheinern, die nix damit zu tun haben. Niemand wird ernsthaft "PORTA" oder ähnliches direkt benutzen. Ein
#define EIERSALAT PORTA
reicht idR nicht aus. Wenn, dann ist angesagt
#define EIERSALAT_PORT PORTA
#define EIERSALAT_PIN PINA
#define EIERSALAT_DDR DDRA
#define EIERSALAT_BIT 5
// oder
#define EIERSALAT_MASK (1<<5)

EIERSALAT_PORT &= ~EIERSALAT_MASK;
EIERSALAT_PORT &= ~(1 << EIERSALAT_BIT);
if (bit_is_set (EIERSALAT_PIN, EIERSALAT_BIT)) {...}


Dein Makro-Konstrukt finde ich ehrlichgesagt ein bischen übertrieben...
das ganze geht nämlich genauso gut auch ohne[...]
Mir ist durchaus bewusst, daß man C programmieren kann ohne einen Präprozessor zu benutzen oder Makros zu definieren.
Meine Makros find ich auch recht fett. Das ist mir aber inzwischen egal, ich benutz die schon lange und auf verschiedenen AVRs und musste seither nix mehr dran ändern.
For allem dann ist's praktisch, wenn es viele Ports sind und ich mit Port-Expandern arbeite. Bei über 50 Ports wird's dann schnell unübersichtlich... Umstellen von einem physikalischen Port auf einen via Expander oder zurück ändert nix an der C-Quelle, sondern nur in der Zuweisung einer anderen Port-Konstanten :-)
Gleichzeitig produzieren die Makros vorbildlich dichten Code.

Daß Programmierstil Geschmackssache ist, steht natürlich ausser Frage.
Zugegebenermassen würd mir so was wie
#define PORT_TASTER PORTD.3
PORT_TASTER.DDR = 0; // als IN
PORT_TASTER.PORT = 1; // mit Pullup
if (0 == PORT_TASTER.PIN) ..., was der OP im Sinn hatte, auch nicht schlecht gefallen, und es war die Motivation, ähnliche Konstrukte zu realisieren.

Javik
15.08.2006, 17:39
Hi!
mir is klar, das das hier jetzt schon nen bisschen älter is, aber ich hätt mal noch ne frage dazu:


Das sieht alles recht kompliziert aus, aber das kann man wie eine Black Box benutzen und muss es nicht verstehen. Diese beiden Dateien verwende ich in allen Projekten. Sie liegen in einem Verzeichnis ausserhalb der Projekte und den Pfad gebe ich gcc mit dem -I Schalter mit. Etwa:
avr-gcc ... -Ic:/avr-projekte/include ...

Was is dieser -I Schalter und wie wird er verwendet?? ich suche nämlich genau diese Funktion, damit ich nicht in jedem Verzeichnis miener verschiedenen Programme immer die gleiche Includedatei haben muss!!
(is ja auch wesentlich aufwendiger, wenn man immer alle includedateien ändern muss wenn man die Includedatei mal verbessert!)

Wäre sehr Dankbar für ne Erklrung des -I Schalters!
Grüße, Javik

linux_80
15.08.2006, 19:20
Hi Javik,
das Beispiel hast Du doch gleich mitzitiert,

avr-gcc ... -Ic:/avr-projekte/include ...

einfach ein -I mit dem Pfad in den gcc aufruf, und schon wird der Pfad beim compilieren miteinbezogen.
Kann man auch im Makefile separat hinterlegen, damit man die Einstellungen wieder findet.

Javik
16.08.2006, 17:03
Hi Linux,

Ja schon ;) aber ich wusst net wo ich das hinschrieben soll ^^
ich habs jetzt gemacht wie du mir geraten hast :) einfach ins Makefile reinschreiben!
da gibts schon ne eigene Stelle... AUGEN AUF ^^ 8-[ :


# Place -I options here
CINCS = -Ic:\Programme\Winavr\....

jetzt kann ich in dem angegebenen Verzeichnis meine ganzen includes reinhaun :D
Super! Danke :)

Gruß Javik

Roberto
04.11.2007, 15:00
Hallo Leute

Der Thread ist schon alt, aber schreibe da gleich dazu .

Irgendwie stehe ich da am Schlauch 8-[
(schon Stunden gesucht...)

Habe bisher unter CodeVison sowas verwendet:
#define rs PORTA.6

Aufruf dann mit:
rs= 1;

Nach Rückumstieg auf WinAVR muss ich das jetzt umschreiben..
8-[

Komme leider mit diesen Logischen Verknüpfungsanweisungen nicht zurecht.

Müsste das in WinAVR dann nicht so heissen:
#define rs PORTA |= (1<<6)

Mag er aber nicht :-k

Will mit Define nur das Bit vom Port zuweisen wo ich dann nachher wieder sagen kann:
rs=1;


Bitte helft mir 8-[
l.G. Roberto

Felix G
04.11.2007, 15:25
das geht so nicht...


aus deinem Code baut der Präprozessor das hier:

PORTA |= (1<<6) = 1;

Dieser Code macht aber keinen Sinn.

Roberto
04.11.2007, 19:37
Hallo Felix

Ja das dachte ich mir, aber wie mache ich es richtig?
l.G. Roberto

Roberto
04.11.2007, 20:34
Ps.:
Habe da jetzt endlich etwas dazu gefunden:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=33832&highlight=define+port+pin

Sehe ich das so richtig?

Ich kann mit #Define nur dem "rs" eine 4 Zuweisen (Bit 4)

Das das Bit 4 gesetzt wird muss ich dann schreiben:

PORTA |= (1 << rs);

Stimmt das so? geht das nicht einfacher ?
So müsste ich ja immer im Code den Port neu angeben :-k

l.G. Roberto

izaseba
04.11.2007, 22:08
Hallo Roberto,
Du irrst aber durch die C Compiler Welt ;-)
/*Offtopic*/
Was war denn so schlimm an CodeVison(Es ist keine Ironie sondern eine ernste Frage)
/*Ende Offtopic*/

Ja ich weiß, Du möchtest irgendwas haben, was immer wieder aus neue einsetzbar ist, ohne viel in Code rumzuwühlen, kann ich gut nachvollziehen!

Ich mache es z.B. so
lcd.h


#define DATADDR DDRA
#define DATAPORT PORTA
#define DATAPIN PINA
#define ENABLEPORT PORTC
#define ENABLEDDR DDRC
#define ENABLE (1<<PC6)
#define RWPORT PORTC
#define RWDDR DDRC
#define RW (1<<PC7)
#define RSPORT PORTG
#define RSDDR DDRG
#define RS (1<<PG2)

dann ein Auszug aus lcd.c


uint8_t get_busy_flag(void) {
uint8_t busy;
DATADDR = 0;
DATAPORT = 0x00;
RWPORT |= RW;
enable();
busy = DATAPIN;
RWPORT &= ~RW;
DATADDR = 0xFF;
if (busy & 0x80)
return 1;
else
return 0;
}

Du siehst also, wenn ich einen anderen µC nehme, oder das ganze anders belege,
brauche ich nur ein paar Änderungen in lcd.h machen :-)
Man kann das auch weiter vereinfachen und zwar liegen die Register PIN* DDR* PORT*
immer beieinander (leider weiß ich nicht mehr aus dem Kopf in welcher Reihenfolge)
dann könnte man einfach nur sowas machen


#define DATAPIN PINC
#define DATADDR DATAPIN+1
#define DATAPORT DATADDR+1

jetzt muß man nur noch 1 Zeile Ändern!
Ich weiß jetzt wirkilch nicht ob die Reihenfolge so stimmt, also nicht drauf verlassen,
lieber in io*.h nachschauen !

Gruß Sebastian

Roberto
04.11.2007, 22:54
Hallo izaseba


Was war denn so schlimm an CodeVison(Es ist keine Ironie sondern eine ernste Frage)
Naja, Codevision weicht leider schon ein bisschen vom Standart-C ab.
Sie machen da Vereinfachungen rein , die sicher auch sehr vorteilhaft sind, aber sie machen dadurch teilweise eine neue Sprache.
Z.B. das jetzt mit Porta.1 oder die andere Sache mit dem String
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=32864&highlight=
, oder die Interrupts sind ganz anders .....

Ich will da nicht wieder das ganze Programm umschreiben , wenn ich dann einmal einen anderen Compiler verwende.
Darum jetzt der Rückschritt auf WInAVR :-b
Und wenn ich schon das lerne, dann vielleicht gleich so, dass ich es dann auch z.B. beim PC anwenden kann..
-----
Also das mit dem Port-Pin zuweisen, ist schon schön umständlich :-s
Statt rs= 0 muss ich so einen Haufen Zeichen schreiben... :-s

Aber egal....
Ich werde es mal so probieren...

Wenn ich deinen Code richtig interpretiere...
Könnte ich auch schreiben:

#define rw (1<<PA6).................(oder halt nur (1<<6)
#define Adressport PORTA

Adressport |= rw; (rw auf 1 setzen)
Adressport &= ~rw; (rw auf 0 setzen)


sehe ich das so richtig ?

l.G. Roberto

izaseba
04.11.2007, 23:15
Also das mit dem Port-Pin zuweisen, ist schon schön umständlich Eh?
Statt rs= 0 muss ich so einen Haufen Zeichen schreiben... Eh?


Ja nun, Du möchtest doch 'standard' C =P~
Und so viel ist es doch nicht, ich habe meine lcd.h mal geschrieben,
und gut ist, davon habe ich noch eine für 4Bit Datenbus.
Es werden nur die ganzen Dateien kopiert, Portzuweisungen geändert,
und danach wieder vergessen.


#define rw (1<<PA6).................(oder halt nur (1<<6)
#define Adressport PORTA

Adressport |= rw; (rw auf 1 setzen)
Adressport &= ~rw; (rw auf 0 setzen)


sehe ich das so richtig ?

Ich würd mal sagen, das siehst Du richtig, ja ;-)

Gruß Sebastian

P.S. die Väter von C haben mal empfohlen solche 'Konstanten' Groß zu schreiben, damit man sie besser im Code von den anderen Sachen unterscheiden kann also anstatt Adressport, ADRESSPORT usw.
Es ist nicht zwingend, macht aber Sinn.
Das wollte ich nur so nebenbei in den Raum werfen.

Roberto
04.11.2007, 23:26
Danke .......... \:D/

Roberto
09.11.2007, 20:08
Hallo (nochmal )

Muss da nochmal nachfragen :-b

Habe da im Tutorial noch was gefunden...
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

Veraltete Funktionen zum Zugriff auf Bits in Registern

cbi und sbi zum Löschen und Setzen von Bits sind nicht mehr erforderlich, der Compiler unterstützt dies ohne diesen Umweg. Die Bezeichnung ist ohnehin irreführend da die Funktionen nur für Register mit Adressen im unteren Speicherbereich tatsächlich in die Assembleranweisungen cbi und sbi übersetzt werden.

// alt:
sbi(PORTB, PB2);
cbi(PORTC, PC1);

// neu (auch nicht mehr wirklich neu...):
PORTB |= (1<<PB2);
PORTC &= ~(1<<PC1);

Will oder kann man den Code nicht portieren, ist zur weiteren Verwendung von sbi und cbi die Header-Datei compat/deprecated.h einzubinden. Wer unbedingt will, kann sich natürlich eigene Makros mit aussagekräftigeren Namen definieren. Zum Beispiel:

#define SET_BIT(PORT, BITNUM) ((PORT) |= (1<<(BITNUM)))
#define CLEAR_BIT(PORT, BITNUM) ((PORT) &= ~(1<<(BITNUM)))
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^= (1<<(BITNUM)))

Kann mir jemand bitte das untere mit Define und mit den Klammern erklären?

Irgendwie will ich noch nicht so recht wahrhaben, dass ich da mit Define nicht meinen Pin angeben kann :-k
Geht das vielleicht irgendwie mit SPI ? oder mit (PORT,BITNUM)... ???

(zur Erinnerung: Möchte sagen: Motorpin = 1;
(vorher definiert mit Motorpin = PORTA und PIN 2))

Vielleicht weis ja doch jemand noch ein Lösung?

Wie macht es den CodeVision?
Da gibt man ja auch an: PORTA.1= 2
Könnte man das irgendwie einfach nachbilden ?

l.G. Roberto

roboterheld
09.11.2007, 20:57
....warum funktioniert die dirktanweisung( PORTC.2=1 ) dann im wrkit bzw. keil das sind doch auch c-compiler?......


das sind schlechte compiler, die sich nicht an den standart halten, sondern machen es so, wie gerade die parteifahne weht. kosten zwar geld, verderben aber den charakter.

mfg

CsT
09.11.2007, 21:14
Hi, die Sache mit dem Bit setzen kannst du mit Hilfe von Makros lösen. Hier eine Lösung, die gut funktionieren sollte:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871

Alles weiterführende wird dann dort erklärt ..

Grüße CsT

Roberto
10.11.2007, 12:15
Hallo Cst (Danke für die Antwort)

Habe mir jetzt mal deinen Link angeschaut.
(Leider kann ich nicht so gut englisch)

Die Definition mit:
#define LED0 SBIT( PORTB, 1 )
Wäre schon toll gewesen, funktioniert aber leider nicht bei mir.
sci und cbi habe ich jetzt auch mal probiert... Geht leider auch nicht.

Schade...

L.G. Roberto