oberallgeier
28.10.2009, 09:28
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 (https://www.roboternetz.de/phpBB2/viewforum.php?f=34&sid=6e9de270920addadb006aab3f003de32) wurde ein hübsches Beispiel zur Erleichterung der Les- und Schreibbarkeit von C durch askazo (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=457999&sid=6e9de270920addadb006aab3f003de32#457999) vorgestellt. Es betrifft einfache Bitoperationen zum Setzen und Löschen von Port-/Registerpins. Hier mal 1:1 kopiert:
Zitatanfang:
#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.
/* >>
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 =====
// ================================================== ==============================
bei zwei sehr kurzen, prinzipiell gleichen Codestücken habe ich Laufzeitunterschiede festgestellt.
Im Thread von copius über die empfohlene Vorgehensweise für Programmaufbau (https://www.roboternetz.de/phpBB2/viewforum.php?f=34&sid=6e9de270920addadb006aab3f003de32) wurde ein hübsches Beispiel zur Erleichterung der Les- und Schreibbarkeit von C durch askazo (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=457999&sid=6e9de270920addadb006aab3f003de32#457999) vorgestellt. Es betrifft einfache Bitoperationen zum Setzen und Löschen von Port-/Registerpins. Hier mal 1:1 kopiert:
Zitatanfang:
#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.
/* >>
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 =====
// ================================================== ==============================