Hallo alle,
bei zwei sehr kurzen, prinzipiell gleichen Codestücken habe ich Laufzeitunterschiede festgestellt.
Im Thread von copius über die empfohlene Vorgehensweise für Programmaufbau wurde ein hübsches Beispiel zur Erleichterung der Les- und Schreibbarkeit von C durch askazo vorgestellt. Es betrifft einfache Bitoperationen zum Setzen und Löschen von Port-/Registerpins. Hier mal 1:1 kopiert:
Zitatanfang:
Code:
#define SetBit(ADDRESS,BIT) ((ADDRESS) |= (1<<(BIT))) //!< Setzt ein bestimmtes Bit eines Registers
#define ClrBit(ADDRESS,BIT) ((ADDRESS) &= ~(1<<(BIT))) //!< Löscht ein bestimmtes Bit eines Registers
#define ToogleBit(ADDRESS,BIT) ((ADDRESS) ^= (1<<(BIT))) //!< Toogelt ein bestimmtes Bit eines Registers
#define IsBitSet(ADDRESS,BIT) (((ADDRESS) & (1<<BIT))?1:0) //!< Fragt ein bestimmtes Bit eines Registers ab
So wird zum Beispiel aus der recht kryptischen Abfrage eines Eingangs auf PB4
if (PINB &(1<<PB4)) {}
ein recht einfaches
if (IsBitSet(PINB,4)) {}
und das ohne dass der Compiler einen anderen Code draus macht. (Ende des Zitates)
Klasse - sagte ich, und verwendete die beiden ersten #defines auch in meinem Programm.
In meinen Programmen habe ich anfangs, vor allem ausführbaren Code - insbesondere vor irgendwelchen Interrupts, eine for-Schleife, die mit rund 10 Hz eine StatusLED 50 x blinken lässt. Das hat sich bei mir in der Entwicklungsphase bewährt, um ungewollte Resets zu erkennen; diese Blinksequenz tritt nur und ausschließlich hier auf.
Leider stellt sich beim Verwenden der hübschen #defines von askazo heraus, das nun die Blinkfrequenz nicht mehr stimmt. Also Suchen und Vergleichen - nicht sooo pfiffig in zwei *.lls mit 144 KB resp. fast 3400 Zeilen. Code gekürzt auf ein Minimum - und verglichen. Vielleicht habe ich ein wichtiges Detail übersehen, aber ich sehe keinen Unterschied. Auffällig ist, dass die beiden Schleifen hintereinander geschrieben mit unterschiedlicher Frequenz blinken.
Frage: Hat bitte jemand eine Erklärung dafür?
Mein Code - vollständig. WinXP-SP3, AVRStudio 4.16, Build 638, mega328 mit 20 MHz, Platine in meinem MiniD0.
Code:
/* >>
Sicherung 28Okt09 0850 ..\C2\tst328_10\tst328_10.c war D01_21x00.c
===================================================================================
Target MCU : ATmega368P
Target Hardware : miniDO = R3D01
Target cpu-frequ. : 20 MHz, externer Quarzoszillator
===================================================================================
Enthaltene Routinen :
void waitms(uint16_t ms) // Delay
int main(void)
===================================================================================
*** Versionsgeschichte:
====================
x10 28Okt09 0850 Test Startblink mit und ohne defines
x00 27Okt09 2330 Test des Startloops
===================================================================================
================================================================================ */
#include <stdlib.h>
#include <avr/io.h>
// #include <avr/interrupt.h>
#define MCU = AVR_ATmega368p
#define F_CPU 20000000 // Quarz 20 Mhz-CPU
// === #defines der PortPins beim 328p ===========================================
// =================================================================================
#define PC5 5 // Pindefinition _ _ _ PortC/PDIP328p nur bis PC5
#define SetBit(ADDR,BIT) ((ADDR) |= (1<<(BIT))) // Setzt Bit
#define ClrBit(ADDR,BIT) ((ADDR) &= ~(1<<(BIT))) // Löscht Bit
// =================================================================================
// ===== Subroutinen =============================================================
// =================================================================================
/*### Programm pausieren lassen !! Der Pausenwert ist nur experimentell !*/
void waitms(uint16_t ms)
{
for(; ms>0; ms--)
{
uint16_t __c = 4000;
__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__c)
: "0" (__c)
);
}
}
/* ============================================================================== */
/* ===== ENDE Subroutinen ================================================== */
/* ============================================================================== */
// =================================================================================
// === HAUPTProgramm ==============================================================
int main(void)
{
uint8_t i;
// Pins/Ports als Ein- (0) oder Ausgänge (1) konfigurieren, Pull Ups (1) aktivieren
// A = Ausgang, E = Eingang ohne , EU = Eingang MIT PullUp
//
DDRC = 0b01110000; // PC3 ist ADC3, PC0 .. 6 , kein PC7-Pin bei m168
PORTC = 0b00000111; // Beachte für ADC: PC3 ist ADC-Eingang ##>> OHNE Pullup !!
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for(i=0; i<50; i++) // gLED auf PC5 i-fach blinken lassen OHNE Interrupts
{
PORTC |= (1<<PC5); // LED auf PC5 schalten EIN, HELL
waitms(3); // ... damit man kurze resets besser erkennt
PORTC &= ~(1<<PC5); // LED auf PC5 schalten AUS, Dunkel
waitms(97);
}
for(i=0; i<50; i++) // gLED auf PC5 i-fach blinken lassen OHNE Interrupts
{
SetBit(PINC, 5); // LED auf PC5 schalten EIN, HELL
waitms(3); // ... damit man kurze resets besser erkennt
ClrBit(PINC, 5); // LED auf PC5 schalten AUS, Dunkel
waitms(97);
}
return 0;
}
// ===== Ende =====
// ================================================================================
Lesezeichen