avr-gcc-list
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [avr-gcc-list] AVR assembly for fast bit bang


From: Jesper Hansen
Subject: Re: [avr-gcc-list] AVR assembly for fast bit bang
Date: Wed, 09 Nov 2005 17:58:42 +0100
User-agent: Mozilla Thunderbird 1.0.6 (Windows/20050716)

Well, it does get a lot faster.
The sbi/sbi instructions are 2 cycles, so it's worth a few words of initial code to get rid of them inside the loop.


#include <avr/io.h>

#define CLOCK_B (1<<0)
#define BIT_B   (1<<1)

void myjunk(uint8_t byte)
{
        register uint8_t i = 8;
        register uint8_t op = PORTA;
        register uint8_t oplc = op | CLOCK_B;
        register uint8_t ophc = op | BIT_B | CLOCK_B;

        while (i--)
        {
                PORTA = oplc;           // clear bit, clock hi
                if( byte & (1<<7) )
                        PORTA = ophc;   // set bit, clock high
                byte <<= 1;
                PORTA &= ~CLOCK_B;  // falling edge of clock
        }
}

Resultant asm :


   0:   98 2f           mov     r25, r24
        register uint8_t i = 8;
        register uint8_t op = PORTA;
   2:   8b b3           in      r24, 0x1b       ; 27
        register uint8_t oplc = op | CLOCK_B;
   4:   38 2f           mov     r19, r24
   6:   31 60           ori     r19, 0x01       ; 1
        register uint8_t ophc = oplc | BIT_B/* | CLOCK_B*/;
   8:   83 60           ori     r24, 0x03       ; 3

        while (i--)
        {
                PORTA = oplc;           // clear bit, clock hi
                if( byte & (1<<7) )
                        PORTA = ophc;   // set bit, clock high
                byte <<= 1;
                PORTA &= ~CLOCK_B;      // falling edge of clock
   a:   27 e0           ldi     r18, 0x07       ; 7
   c:   3b bb           out     0x1b, r19       ; 27
   e:   97 fd           sbrc    r25, 7
  10:   8b bb           out     0x1b, r24       ; 27
  12:   99 0f           add     r25, r25
  14:   d8 98           cbi     0x1b, 0 ; 27
  16:   21 50           subi    r18, 0x01       ; 1
  18:   c8 f7           brcc    .-14            ; 0xc
  1a:   08 95           ret


The optimization -Os or -O3 will give this, regular -O is one word longer, as it does not use the sbrc instruction.
The inner loop in this variant is about twice as fast as David's code.
It is however unbalanced, and sending zeroes are faster than sending ones.

/Jesper


Mike S. wrote:
Hello to all,
My code is:

#define ADS1210_PORT PORTF
#define SDIO_BIT     0x04 /* 0b0000 0100 PORTF.2*/
#define CLK_BIT      0x02 /* 0b0000 0010 PORTF.1*/
#define SDOUT_BIT    0x01 /* 0b0000 0001 PORTF.0*/

#define SDIO_LOW    ADS1210_PORT &= ~SDIO_BIT
#define SDIO_HIGH   ADS1210_PORT |=  SDIO_BIT
#define CLK_LOW   ADS1210_PORT &= ~CLK_BIT
#define CLK_HIGH  ADS1210_PORT |=  CLK_BIT
/*----------------------------------------------------------------------------+
| Function: write_data                                                        |
|                                                                             |
| Args:     n_bits                                                            |
|                                                                             |
| Action:  Writes <n_bits> in the device SDIO input pin                       |
|                                                                             |
| Efects:                                                                     |
|                                                                             |
+----------------------------------------------------------------------------*/
void write_data (Word towrite, Byte nbits)
{
  Byte n;

  for(n = 0; n < nbits; n++)
  {

    CLK_HIGH;
    if( towrite & (0x0001 << n))
    {
      SDIO_HIGH;
    }
    else
    {
      SDIO_LOW;
    }
    CLK_LOW;

  }
}

I haven't decided yet if I will use the AT90CAN128 SPI module or do it
by bit bang! My uC runs at 16MHz. The device I'm interfacing with
supports serial clock rates up to 2 MHz. In the past, I already made a
driver for this device for the Philips XA-S3 in C and in Assembler
(the asm was for the bit bang part), and now I want to port that
driver to the AVR. The problem is that I don't have a lot of
experience with the AVR assembly! That is why I'm asking this question
(wrong guess David Kelly, but thanks for the reply). BTW, is your code
snippet correct?
Thanks for the reply Daniel O'Connor, but I usually don't use the
optimization until I try a couple of and optimization techniques. I
already had some bad experiences with the optimization in some Texas
Instruments DSPs...
I use AVR-GCC.


Thanks to all replies.

Thanks in advance
Bruno Miguel


On 11/9/05, David Kelly <address@hidden> wrote:

On Tue, Nov 08, 2005 at 03:45:54PM +0000, Mike S. wrote:

Hello to all,
Can anyone tell me the best (faster) way to implement bit shifting
(serial synch protocol -in a bit bang fashion-) with two general
purpose digital pins (one pin for data the other for clock)? Using C
is not fast enough! I need assembly!

Sounds like a homework assignment. Smart instructors monitor this list.

People keep saying "C isn't fast enough." I don't belive it. First
attempt:

#include <avr/io.h>

#define CLOCK_B (1<<0)
#define BIT_B   (1<<1)

void
myjunk(uint8_t byte) {
        uint8_t i;

        for( i = 0 ; i < 8 ; i++ ) {
                PORTA |= CLOCK_B;       //  rising edge of clock
                if( byte & (1<<7) )
                        PORTA |= BIT_B;         // set
                else
                        PORTA &= ~BIT_B;        // clear
                byte <<= 1;
                PORTA &= ~CLOCK_B;      //  falling edge of clock
        }
}

Compiled with
"avr-gcc -O -gdwarf-2 -Wall -ffreestanding -mmcu=atmega64 -c junk.c"

Output of "avr-objdump -S junk.o":

myjunk(uint8_t byte) {
        uint8_t i;

        for( i = 0 ; i < 8 ; i++ ) {
   0:   90 e0           ldi     r25, 0x00       ; 0
                PORTA |= CLOCK_B;       //  rising edge of clock
   2:   d8 9a           sbi     0x1b, 0 ; 27
                if( byte & (1<<7) )
   4:   88 23           and     r24, r24
   6:   14 f4           brge    .+4             ; 0xc <myjunk+0xc>
                        PORTA |= BIT_B;         // set
   8:   d9 9a           sbi     0x1b, 1 ; 27
   a:   01 c0           rjmp    .+2             ; 0xe <myjunk+0xe>
                else
                        PORTA &= ~BIT_B;        // clear
   c:   d9 98           cbi     0x1b, 1 ; 27
                byte <<= 1;
   e:   88 0f           add     r24, r24
                PORTA &= ~CLOCK_B;      //  falling edge of clock
  10:   d8 98           cbi     0x1b, 0 ; 27
  12:   9f 5f           subi    r25, 0xFF       ; 255
  14:   98 30           cpi     r25, 0x08       ; 8
  16:   a8 f3           brcs    .-22            ; 0x2 <myjunk+0x2>
  18:   08 95           ret


Its not going to get much better than that.

--
David Kelly N4HHE, address@hidden
========================================================================
Whom computers would destroy, they must first drive mad.




_______________________________________________
AVR-GCC-list mailing list
address@hidden
http://lists.nongnu.org/mailman/listinfo/avr-gcc-list






reply via email to

[Prev in Thread] Current Thread [Next in Thread]