Gør jeg det forkert? - Blinke-eksemplet fra Arduino, genskabt i ren assembler (Læst 4554x)

Offline kjoller

  • U=I*R
  • ***
  • Indlæg: 45
  • Antal brugbare Indlæg: 8
    • Vis profil
    • The Electronic Map Room
Hej folkens

Jeg er for sjov begyndt at rode med assembler til AVR. Det er selvfølgelig smartest når man ikke har så meget plads at gøre godt med og/eller har applikationer hvor timing er kritisk, men lige nu prøver jeg bare at knække nøden.

Jeg har prøvet at implementere et program hvor jeg blinker med LED'en på pin Arduino pin 13 (Svarende til PB5 - Port B, bit 5). Målet var at LED skulle skifte status hvert sekund. Så vidt jeg kan lure, så skulle det gerne være perfekt timet på 1.000.000 mikrosekunder (såfremt krystallen er tunet på præcis 16MHz). Det fylder 44 bytes, set i forhold til 1.084 bytes hvis man compiler det basale blink-eksempel fra Arduino - så kan man altid diskutere læsbarheden :)

Er der en assembler-haj, der kan fortælle om jeg er helt i skoven? Jeg er som sagt helt ny i assembler-verdenen. (Hvis ikke jeg kan få hjælp, så er det ren blær - jeg er sgu ret stolt over at reducere et programs størrelse med faktor 25  ;D )

Kode:
Kode:
; My first piece of assembler code. It is made to mimic the blink
; example from the Arduino environment. The arduino example compiles
; to around 1Kb - this is 44 bytes. And should be very
; precise in the timing.
.nolist;
.include "m88def.inc";
.list;


; SETTING CLOCK SPEED - currently at 16 MHz
.equ clockCyclesPerMilliSecond = 16*1000
; The delay to put between blinks in milliseconds
.equ delayMilliseconds = 1000
; The direction register, the port and the bit to set the pin of the
; LED to flash
; Currently at PB5 (Arduino pin 13)
.equ DDR = DDRB
.equ PORT = PORTB
.equ BIT = 5

; SETTING UP REGISTERS
.DEF my_register = R16

; Not sure if this is needed... It works without it.
rjmp setup

; setup
setup:
    SBI     DDR,BIT         ; Set pin to output

loop:
    sbi     PORT,BIT        ; 2 cycles - set pin HIGH
    rcall   Delay           ; 3 cycles (the call itself)
    cbi     PORT,BIT        ; 2 cycles - set pin LOW
    rcall   Delay           ; 3 cycles (the call itself)
    rjmp    loop            ; 2 cycles (the jump itself) - repeat
   
Delay:
    nop
    ; Delay consists of two loops - the inner loop loops for a
    ; millisecond, the outer counts the number of milliseconds-
    ; From every inner loop, there is subtracted the number of
    ; cycles to complete the outer loop (8). From the first time, there
    ; is also subtracted the number of cycles to call, setup and return
    ; from the subroutine as well as the cycles for switching the pin,
    ; half of the rjmp command and the nop in the start of this function
   
    ; inner loop : 4 cycles
    ; outer loop : 8 cycles
    ; pin switching, calling, returning and looping : 16 cycles
   
    ; Since precision is made by cutting the number of times the
    ; inner loop runs, it is important that the number of cycles
    ; in the outer loop and the one-time-fluff is divisble by 4.
   
    ldi     ZH,HIGH((clockCyclesPerMilliSecond-8-16)/4)
    ldi     ZL,LOW((clockCyclesPerMilliSecond-8-16)/4)
    ; A lot of nops and grief could be saved by only supporting a
    ; maximum of 255 millisecond delay.
    ldi     YL,LOW(delayMilliseconds)
    ldi     YH,HIGH(delayMilliseconds)
   
    delayloop:
            sbiw    ZL, 1       ; 2 cycles
            brne    delayloop   ; 2 cycles
       
        sbiw    YL,1                                     ; 2 cycles
        ldi     ZH,HIGH((clockCyclesPerMilliSecond-8)/4) ; 1 cycle
        ldi     ZL,LOW((clockCyclesPerMilliSecond-8)/4)  ; 1 cycle
        nop ; added to make a number of cycles divisible by 4 1 cycle 
        nop ; added to make a number of cycles divisble by 4  1 cycle
        brne    delayloop                                ; 2 cycles
   
    nop ; added to make a number of cycles divisible by 4 ; 1 cycle
    ret ;                                                   3 cycles

 

Offline gerd

  • Administrator
  • µProcessoren
  • *****
  • Indlæg: 915
  • Antal brugbare Indlæg: 97
    • Vis profil
    • Hjemmeside med nogle af mine projekter


Jeg håber, jeg finde en C-kode, som er så kort som din ;)

« Senest Redigeret: Marts 27, 2014, 07:39:32 af gerd »

 

Offline kjoller

  • U=I*R
  • ***
  • Indlæg: 45
  • Antal brugbare Indlæg: 8
    • Vis profil
    • The Electronic Map Room
Jeg prøvede lige at skrive noget ren AVR-GCC-kode der gjorde det samme. Det gav 68 bytes. Så det er godt tæt på ift. hvor meget mere overskueligt det er. Og det kan sikkert også optimeres lidt på.

Jeg har siden fået det ned på 34 bytes - 30, hvis man accepterer at være 4 mikrosekunder ved siden af i timingen :)

 

Offline gerd

  • Administrator
  • µProcessoren
  • *****
  • Indlæg: 915
  • Antal brugbare Indlæg: 97
    • Vis profil
    • Hjemmeside med nogle af mine projekter
I aftes havde jeg også en AVR-GCC kode med 68 bytes. Og sener en idé at reducere den til omkring 45.

.... og du har nu 34/30 ? .... puhhh ....  :P  :o ???
« Senest Redigeret: Marts 27, 2014, 09:29:33 af gerd »

 

Offline gerd

  • Administrator
  • µProcessoren
  • *****
  • Indlæg: 915
  • Antal brugbare Indlæg: 97
    • Vis profil
    • Hjemmeside med nogle af mine projekter
28

yeaaahhhhh !

 8) 8) 8) 8) 8) 8) 8) 8) 8) 8) 8)

 

Offline gerd

  • Administrator
  • µProcessoren
  • *****
  • Indlæg: 915
  • Antal brugbare Indlæg: 97
    • Vis profil
    • Hjemmeside med nogle af mine projekter

 

Offline kjoller

  • U=I*R
  • ***
  • Indlæg: 45
  • Antal brugbare Indlæg: 8
    • Vis profil
    • The Electronic Map Room
Wow!

Er det stadig i C?

Og du kan stadig angive et antal millisekunder - som holder nogenlunde præcist? Eller er det bare blink?

Argh! Så har jeg weekenden for mig! Pokkers, jeg har planer det meste af tiden.

 

Offline gerd

  • Administrator
  • µProcessoren
  • *****
  • Indlæg: 915
  • Antal brugbare Indlæg: 97
    • Vis profil
    • Hjemmeside med nogle af mine projekter
14
Ja, stadig i C.

Der her er min 28-bytes-code:
Kode:
// Compile with avr-gcc gcc-vs-asm_28.c -Wa,-alhd=dump.s -Os

// Define some special function registers. Normally this is done
// with #include <avr/io.h>
#define DDRB    (*(volatile unsigned char *)((0x17) + 0x20))
#define PORTB   (*(volatile unsigned char *)((0x18) + 0x20))

// Define some usefull macros
#define SetBit(x,y) (x |= (1<<y))
#define ClrBit(x,y) (x &= ~(1<<y))

// Delay Makros
#define clockCyclesPer4MilliSecond  ((unsigned int)16*(unsigned int)1000*(unsigned int)4)
#define delay4Milliseconds 250

// Some project defines
#define TESTPIN 5

// The Delay Function
void Delay (void) {
unsigned char outer_i;
unsigned int inner_i;
outer_i = delay4Milliseconds;
do {
inner_i = ((clockCyclesPer4MilliSecond-8)/4);
do {
// There must be a ";", if not, the optimizer
// removes the useless loop
__asm(";");
} while (--inner_i);
} while ( --outer_i);

}


// The main
void main (void) {

// Setup
SetBit(DDRB, TESTPIN);

// The main loop
while (1) {
SetBit(PORTB, TESTPIN); // 2 cycles - set pin HIGH
Delay(); // 3 cycles (the call itself)
ClrBit(PORTB, TESTPIN); // 2 cycles - set pin LOW
Delay(); // 3 cycles (the call itself)
} // 2 cycles (the jump itself) - repeat
}

Og det her er resultatet:
Kode:
00000000 <Delay>:
   0: 2a ef        ldi r18, 0xFA ; 250
   2: 8e e7        ldi r24, 0x7E ; 126
   4: 9e e3        ldi r25, 0x3E ; 62
   6: 01 97        sbiw r24, 0x01 ; 1
   8: f1 f7        brne .-4      ; 0x6 <__zero_reg__+0x5>
   a: 21 50        subi r18, 0x01 ; 1
   c: d1 f7        brne .-12      ; 0x2 <__zero_reg__+0x1>
   e: 08 95        ret

00000010 <main>:
  10: bd 9a        sbi 0x17, 5 ; 23
  12: c5 9a        sbi 0x18, 5 ; 24
  14: f5 df        rcall .-22      ; 0x0 <Delay>
  16: c5 98        cbi 0x18, 5 ; 24
  18: f3 df        rcall .-26      ; 0x0 <Delay>
  1a: fb cf        rjmp .-10      ; 0x12 <main+0x2>

Jeg har en 4ms loop, i stedet for 1ms, så min "delayMilliseconds" er nu en "delay4Milliseconds" og værdien er 250. Der er nu en "unsigned char".


og det her er min 14-bytes-code:


Kode:
// The main
void main (void) {
unsigned char i;
unsigned int loopcnt;

// The main loop
while (1) {

// Increment i.
// Every 128th cycle, bit 7 of i toggles.
// Put i to the PORTB, only Pin7 is an output
i++;

// Because PORTB is initialized with 0, the LED must be
// connected betwenn PORTB7 and Vcc
DDRB = i;

// 16MHz / 128 / 4 = 31250
loopcnt = 31250;
do {
__asm(";");
} while (--loopcnt);
}
}

Kode:
00000000 <main>:
   0: 2f 5f        subi r18, 0xFF ; 255
   2: 27 bb        out 0x17, r18 ; 23
   4: 82 e1        ldi r24, 0x12 ; 18
   6: 9a e7        ldi r25, 0x7A ; 122
   8: 01 97        sbiw r24, 0x01 ; 1
   a: f1 f7        brne .-4      ; 0x8 <__zero_reg__+0x7>
   c: f9 cf        rjmp .-14      ; 0x0 <main>

I stedet for at sætte en bit i PORTB jeg gør det i DDRB. Jeg har kun en "loop". Det er muligt fordi bit 7 af "i" ændrer hver 128.  løb. Og jeg har alt i main.c

EDIT: Det var alt teoretisk. Jeg har ikke prøvet det.
« Senest Redigeret: Marts 27, 2014, 21:43:13 af gerd »