Interrupts and inline assembler

Go To Last Post
50 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm struggling to understand inline assember in avrgcc.  I'm not sure what my mental block is, but I've read the inline assembler cookbook and still feel a bit lost.

 

For speed reasons, I want to code a Pin Change interrupt in inline assembler.  The idea is to modify a global flag when an interrupt occurs that can then be checked and handled by the main code.  I get most of this, but I'm concerned about pushing and popping the right registers before and after my code so that I don't disrupt my main code.  I just can't seem to figure out how to determine which registers to push and pop.

 

I'm not sure that makes sense, so I've coded up a simple example of what I'm trying to do.  My actual program is a bit more complex, but this should get across the basics of what I'm trying to do:

 

// Naked interrupt handler example
// Targeting an attiny85

#include<avr/interrupt.h> 
#include<avr/io.h>

static const uint8_t LED_PIN = PB4; // Physical pin 3
static const uint8_t IO_PIN = PB3; // Physical pin 2

volatile bool g_interrupt = false;

int main()
{
    // Configure the LED pin as an output
    DDRB = (1<<LED_PIN);

    // Enable pin change interrupts on the IO pin
    GIMSK |= (1<<PCIE);
    PCMSK |= (1<<IO_PIN);

    // Enable interrupts globally
    sei();

    while(true)
    {
        // Toggle the LED if an interrupt has occurred
        if (g_interrupt == true)
        {
            g_interrupt = false;
            PORTB ^= (1<<LED_PIN);
        }
    }
}

// Pin change interrupt handler
ISR(PCINT0_vect, ISR_NAKED)
{
    asm volatile
    (
        "cli\n\t"                        // Disable interrupts
//        "   push ???\n\t"                // Push the register onto the stack
        "   sbic    %[port], %[pin]\n\t" // Check if the pin is high
        "   ldi     %[flag], 1\n\t"      // Set interrupt flag to true
//        "   pop ???\n\t"                 // Pop the register from the stack  
        "reti"                           // Return from the interrupt

        : [flag] "=d" (g_interrupt)
        : [port] "I" (_SFR_IO_ADDR(PINB)), [pin] "I" (IO_PIN)
        :
    );
}

This works, but I have no idea what register my "flag" variable is using, so I can make sure to save and restore it before my interrupt returns.  This example works as-is, but I don't think I can trust it in a more complex program.

 

What am I missing?  Is there a better way to do this?  What information have I left out?

 

I heartily appreciate any advice.

Last Edited: Tue. Nov 16, 2021 - 03:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

If you want to do assembly just for an ISR, why not just simply write the ISR in assembly. Avoid inline assembly.

 

Add new .s file to your project.

 

 

 

 

Remove this C ISR code from your main.c file.

ISR(PCINT0_vect, ISR_NAKED)
{
    asm volatile
    (
        "cli\n\t"                        // Disable interrupts
//        "   push ???\n\t"                // Push the register onto the stack
        "   sbic    %[port], %[pin]\n\t" // Check if the pin is high
        "   ldi     %[flag], 1\n\t"      // Set interrupt flag to true
//        "   pop ???\n\t"                 // Pop the register from the stack
        "reti"                           // Return from the interrupt

        : [flag] "=d" (g_interrupt)
        : [port] "I" (_SFR_IO_ADDR(PINB)), [pin] "I" (IO_PIN)
        :
    );
}

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?” - Brian W. Kernighan
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint-Exupery

Last Edited: Tue. Nov 16, 2021 - 06:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Firstly: there is no need to disable interrupts inside an ISR. They get disabled automatically by the AVR CPU.

Secondly: If you are going to write an assembly it is an absolute requirement you actually know and are familiar with the instruction set. LDI works on a subset of registers NOT memory.

 

Spend a day to so reading this: AVR Instruction Set Manual It will be time well spent.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:
I'm struggling to understand inline assember in avrgcc

Yes, it is arcane (and not just in GCC) - so why cause yourself the grief?

 

See: https://www.avrfreaks.net/commen...

 

bradk3 wrote:
I want to code a Pin Change interrupt in inline assembler.

So why not just do that in an assembler file?

 

Being an interrupt, it will never be called by C code - so you don't have to worry about the C call-return conventions ...

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Tue. Nov 16, 2021 - 09:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

bradk3 wrote:
I just can't seem to figure out how to determine which registers to push and pop.

But that's nothing specifically to do with inline assembler - that's just programming basics.

 

The whole point of an interrupt, as the name suggests, is that it interrupts your other code - so it must leave all registers in the same state that it found them.

 

In other words, any & all registers that your ISR changes have to be saved and restored to their previous values

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 3

bradk3 wrote:

Is there a better way to do this?

 

Yes. Write it in C. It's unlikely, given how simple your proposed ISR is, that'll you'll do much better than the compiler.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Brian Fairchild wrote:
It's unlikely ... that'll you'll do much better than the compiler.

+1

 

but, if you really feel you can out-do the compiler, then just take the compiler's generated assembler as your starting point to create your own assembler routine...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2


The suggestion of moving to .S (not .s for obvious reasons!!) is a great one but let's take a step back. If I replace your original ISR() with:

ISR(PCINT0_vect) {
    if (PINB & (1 << IO_PIN)) {
        g_interrupt = true;
    }
}

I get:

ISR(PCINT0_vect) {
  68:	1f 92       	push	r1
  6a:	0f 92       	push	r0
  6c:	0f b6       	in	r0, 0x3f	; 63
  6e:	0f 92       	push	r0
  70:	11 24       	eor	r1, r1
  72:	8f 93       	push	r24
    if (PINB & (1 << IO_PIN)) {
  74:	b3 9b       	sbis	0x16, 3	; 22
  76:	03 c0       	rjmp	.+6      	; 0x7e <__vector_2+0x16>
        g_interrupt = true;
  78:	81 e0       	ldi	r24, 0x01	; 1
  7a:	80 93 60 00 	sts	0x0060, r24	; 0x800060 <__DATA_REGION_ORIGIN__>
    }
  7e:	8f 91       	pop	r24
  80:	0f 90       	pop	r0
  82:	0f be       	out	0x3f, r0	; 63
  84:	0f 90       	pop	r0
  86:	1f 90       	pop	r1
  88:	18 95       	reti

Presumably it is the R1 and SREG stuff you are trying to avoid. If you use a recent GCC and Binutils you will find you don't get this anyway so that is one potential solution.

 

If you are using Studio 7 you are probably currently using 5.4.0 which gives:

 

But if you use a later GCC (on Godbolt anything from 9.2.0 onwards):

 

 

But just take a step back and consider this. WHY do you want to do this with interrupt anyway? It takes 12..20 cycles to just get into/out of an ISR let alone the code it contains. As you are doing nothing else in main if you want real speed of response:

#include <stdbool.h>
#include<avr/io.h>

static const uint8_t LED_PIN = PB4; // Physical pin 3
static const uint8_t IO_PIN = PB3; // Physical pin 2

int main()
{
    // Configure the LED pin as an output
    DDRB = (1<<LED_PIN);

    while(true)
    {
        // Toggle the LED if an interrupt has occurred
        if (PINB & (1 <<IO_PIN))
        {
            PORTB ^= (1<<LED_PIN);
        }
    }
}

Often polling in main() (whether it be an IO pin or an IF flag for some peripheral (like timer overflow or something) is far quicker and more efficient than needlessly involving an ISR.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
If you are using Studio 7 you are probably currently using 5.4.0

Why Atmel Microchip studio ships with 5.4.0 avrgcc and not anything beyond?

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?” - Brian W. Kernighan
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint-Exupery

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Heisen wrote:
Why Atmel Microchip studio ships with 5.4.0 avrgcc and not anything beyond?
Because it's stable.

 

They used to have an incentive to keep shipping updated compilers because each time they added new models they needed a new issue of the compiler that would understand the additional device names passed to -mmcu= and a new version of AVR-LibC with the headers and libs for the new device.

 

Then packs and -B came along and now they can issue the support fore new devices simply by delivering new pack entries - so there's no real incentive to update compiler and standard library any more.

 

I guess they will eventually roll out a new one to take advantage of new features and important bug fixes but there's nothing beyond 5.4.0 that really counts as "must have".

 

Also one has to look at the overall Microchip strategy. They are trying to wean people off Studio and avr-gcc anyway. They want to put all their effort in MPLABX and their "xc8" payware so there may not be much drive (read development dollars) to do very much more with Studio or avr-gcc.

 

For a time there was also a threat that AVR was being dropped from GCC anyway but hopefully that has passed for now.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you want to do it like that just pick a register (e.g., r24) and let the compiler know (i.e., after the third colon list "r24").

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But as the compiler's code shows you don't set the flag with LDI alone (*unless* you have bound it to a reserved register (which g_interrupt is not)) 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Heisen wrote:

 

If you want to do assembly just for an ISR, why not just simply write the ISR in assembly. Avoid inline assembly.

 

Add new .s file to your project.

 

I honestly didn't think about this even being possible, but it makes a lot of sense.  Regardless of whether I use it with this current project, this is bound to be useful at some point.  I'm developing on Linux and haven't found a nice IDE to use yet (I should try Eclipse or Code::Blocks, I suppose), but I should be able to figure out how to mix assembly and C files in my Makefile.

 

Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:

Firstly: there is no need to disable interrupts inside an ISR. They get disabled automatically by the AVR CPU.

Secondly: If you are going to write an assembly it is an absolute requirement you actually know and are familiar with the instruction set. LDI works on a subset of registers NOT memory.

 

Spend a day to so reading this: AVR Instruction Set Manual It will be time well spent.

 

 

Thank you for the tip about interrupts being disabled automatically.  I remember reading that somewhere, but wasn't sure it applied to "naked" ISRs.  Obviously, if it's done in hardware, it's completely unnecessary for me to mess with disabling and enabling interrupts.  I appreciate that!

 

I guess I assumed that my "g_interrupt" variable was being assigned to a register in that inline assembly block somehow.  That code I posted, using LDI to set the variable value to 1, works as-is.  At least, when I use another chip to pulse the pin I'm looking at, my LED toggles, as expected.  This is what lead me to worry about *which* register is being used so I can save and restore it.

 

Regardless, I do really need to dive into the AVR Instruction Set Manual rather than just skimming it.  Thank you again!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:
(I should try Eclipse or Code::Blocks, I suppose),
Both good but now that Microchip's acquisition of Atmel means that there's not only Studio for Windows but MPLABX for multi-platform and that includes Linux I would suggest MPLABX is the most well-engineered (for AVR) IDE on Linux.

 

(I can't remember but I seem to recall that MPLABX is, itself, based on Netbeans which would be another contender alongside Eclips and C::B anyway).

 

As for working with Makefiles. If you are doing Makefile with AVR then I'd hope you might have stumbled upon "Mfile": https://www.sax.de/~joerg/mfile/ - that builds a project specific makefile for your project from a generic template. Part of that template is:

# List C source files here. (C dependencies are automatically generated.)
SRC = $(TARGET).c


# List C++ source files here. (C dependencies are automatically generated.)
CPPSRC =


# List Assembler source files here.
#     Make them always end in a capital .S.  Files ending in a lowercase .s
#     will not be considered source files but generated files (assembler
#     output from the compiler), and will be deleted upon "make clean"!
#     Even though the DOS/Win* filesystem matches both .s and .S the same,
#     it will preserve the spelling of the filenames, and gcc itself does
#     care about how the name is spelled on its command-line.
ASRC =

So you would add any filename.S to the ASRC line. Then that is used at:

# Define all object files.
OBJ = $(SRC:%.c=$(OBJDIR)/%.o) $(CPPSRC:%.cpp=$(OBJDIR)/%.o) $(ASRC:%.S=$(OBJDIR)/%.o) 

then further at:

# Assemble: create object files from assembler source files.
$(OBJDIR)/%.o : %.S
	@echo
	@echo $(MSG_ASSEMBLING) $<
	$(CC) -c $(ALL_ASFLAGS) $< -o $@

which makes use of:

# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)

but, to be honest, the thing to do is simply to use the template in which case pretty much all you have to do is list the source files at the top.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

bradk3 wrote:
I'm struggling to understand inline assember in avrgcc

Yes, it is arcane (and not just in GCC) - so why cause yourself the grief?

 

See: https://www.avrfreaks.net/commen...

 

bradk3 wrote:
I want to code a Pin Change interrupt in inline assembler.

So why not just do that in an assembler file?

 

Being an interrupt, it will never be called by C code - so you don't have to worry about the C call-return conventions ...

 

Honestly, this is probably the advice I should follow.  The only difficulty I can see is I don't understand how to reference a C variable (my "g_interrupt" flag) in a separate assembly file, but I should be able to figure that out.

 

awneil wrote:

bradk3 wrote:
I just can't seem to figure out how to determine which registers to push and pop.

But that's nothing specifically to do with inline assembler - that's just programming basics.

 

The whole point of an interrupt, as the name suggests, is that it interrupts your other code - so it must leave all registers in the same state that it found them.

 

In other words, any & all registers that your ISR changes have to be saved and restored to their previous values

 

Agreed.  This was my concern in the first place with the inline assembly code.  But, if I move my interrupt into an assembly file, I'll have control of which registers I use and can leave them in their original state when I'm done.  

 

Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:

bradk3 wrote:

Is there a better way to do this?

 

Yes. Write it in C. It's unlikely, given how simple your proposed ISR is, that'll you'll do much better than the compiler.

awneil wrote:

 

Brian Fairchild wrote:
It's unlikely ... that'll you'll do much better than the compiler.

+1

 

but, if you really feel you can out-do the compiler, then just take the compiler's generated assembler as your starting point to create your own assembler routine...

 

I'm generally in favor of getting out of the compiler's way and not trying to out-perform it.  Trying to write this in assembly never really felt right.

 

My concern is that I'm declaring this ISR "naked" and my understanding is that doing that shifts a lot more of the responsibility on me.  Is the compiler smart enough to know that it needs to take care of restoring the registers used in the ISR when I ham-string it by declaring it "naked"?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:
My concern is that I'm declaring this ISR "naked" and my understanding is that doing that shifts a lot more of the responsibility on me.

AIUI, It shifts the entire responsibility on to you:

 

https://gcc.gnu.org/onlinedocs/gcc/AVR-Function-Attributes.html

 

Again, either do it in true C, or do it in true assembler - why mess with some half-baked mish-mash?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:
The only difficulty I can see is I don't understand how to reference a C variable (my "g_interrupt" flag) in a separate assembly file, but I should be able to figure that out.

I'd look at the compiler-generated assembler - the compiler knows how to do it ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

The suggestion of moving to .S (not .s for obvious reasons!!) is a great one but let's take a step back. If I replace your original ISR() with:

ISR(PCINT0_vect) {
    if (PINB & (1 << IO_PIN)) {
        g_interrupt = true;
    }
}

I get:

ISR(PCINT0_vect) {
  68:	1f 92       	push	r1
  6a:	0f 92       	push	r0
  6c:	0f b6       	in	r0, 0x3f	; 63
  6e:	0f 92       	push	r0
  70:	11 24       	eor	r1, r1
  72:	8f 93       	push	r24
    if (PINB & (1 << IO_PIN)) {
  74:	b3 9b       	sbis	0x16, 3	; 22
  76:	03 c0       	rjmp	.+6      	; 0x7e <__vector_2+0x16>
        g_interrupt = true;
  78:	81 e0       	ldi	r24, 0x01	; 1
  7a:	80 93 60 00 	sts	0x0060, r24	; 0x800060 <__DATA_REGION_ORIGIN__>
    }
  7e:	8f 91       	pop	r24
  80:	0f 90       	pop	r0
  82:	0f be       	out	0x3f, r0	; 63
  84:	0f 90       	pop	r0
  86:	1f 90       	pop	r1
  88:	18 95       	reti

Presumably it is the R1 and SREG stuff you are trying to avoid. If you use a recent GCC and Binutils you will find you don't get this anyway so that is one potential solution.

 

The R1 and SREG stuff is exactly what I'm trying to avoid.  I'm not sure which version of GCC I'm using, but I'll check when I get home.  From what you showed, updating my version may be exactly what I need.

 

clawson wrote:

But just take a step back and consider this. WHY do you want to do this with interrupt anyway? It takes 12..20 cycles to just get into/out of an ISR let alone the code it contains. As you are doing nothing else in main if you want real speed of response:

#include <stdbool.h>
#include<avr/io.h>

static const uint8_t LED_PIN = PB4; // Physical pin 3
static const uint8_t IO_PIN = PB3; // Physical pin 2

int main()
{
    // Configure the LED pin as an output
    DDRB = (1<<LED_PIN);

    while(true)
    {
        // Toggle the LED if an interrupt has occurred
        if (PINB & (1 <<IO_PIN))
        {
            PORTB ^= (1<<LED_PIN);
        }
    }
}

Often polling in main() (whether it be an IO pin or an IF flag for some peripheral (like timer overflow or something) is far quicker and more efficient than needlessly involving an ISR.

 

Ultimately, I'm attempting to implement a "simple" one-wire communication protocol between two AVRs based on what is used by the WS2812 RGB LEDs, which requires some tight timing.  Essentially, I need to be able to detect a short pulse on the pin as a "0" and a longer pulse as a "1".  My example, obviously, doesn't do that, but I'm trying to build up to it.  

 

Since I'll eventually be doing more in the main loop of my program, I'm not sure polling will be fast enough to catch these pulses, which is why I'm looking at doing this with an interrupt.

 

But, you've given me a different option to consider.  Thank you for taking the time!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MattRW wrote:

If you want to do it like that just pick a register (e.g., r24) and let the compiler know (i.e., after the third colon list "r24").

 

Will the compiler know to restore this register before returning from the interrupt?  If so, that's perfect.  I admit that I don't really understand the usage of that "clobber" register list.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


bradk3 wrote:
  The only difficulty I can see is I don't understand how to reference a C variable (my "g_interrupt" flag) in a separate assembly file, but I should be able to figure that out.
If there's ever anything in the assembler you don't know ask C how to do it. Simply write (just a light framework) of what you want to do in C, compile it with the -save-temps option, look at the .s file that generates.

 

For example:

 

When I build that with -save-temps I get:

 

 

There's a lot of that which is not relevant but the core is:

	.file	"testAsm.c"
__tmp_reg__ = 0
__zero_reg__ = 1

	.text

	.section	.text.foo,"ax",@progbits
.global	foo
	.type	foo, @function
foo:
	sts g_interrupt,__zero_reg__
	ret
	.size	foo, .-foo

The key "takeaway" from this is the very fact that while a symbol (variable or function name being exposed from a .S file does need a ".global" to make it's name public) the converse is not true for imported symbols. The assembler will assume that anything not defined local is external anyway. In fact you can see this in:

 

 

The "U" says it is "undefined" which is a message to the Linker to say "when you are linking you need to look for the "g_interrupt" that this references in some other file". The other side of the same coin is seen in:

 

The "B" says this is creating a BSS variable called "g_interrupt". At link time the linker will be happy to match up these two things.

 

Bottom line: just reference "g_interrput" in your .S and it will be found (unlike C there is no concept of "type" in Asm, everything is just the address of something so you don't need an "extern" declaration as it's just an "unknown name" that will be found by the linker).

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
But as the compiler's code shows you don't set the flag with LDI alone (*unless* you have bound it to a reserved register (which g_interrupt is not)) 

Trying to recap:  OP wants a skinny ISR.  Asked about inline assembler.

 

bradk3 wrote:
For speed reasons, I want to code a Pin Change interrupt in inline assembler.  The idea is to modify a global flag when an interrupt occurs that can then be checked and handled by the main code. 

Respondents indeed spoke of the magic incantations and eye-of-newt needed.  And went on to suggest a separate source file as a better/suggested approach.

 

But let's take note of the elephant in the room-- setting aside the source mechanics for a moment, what sequence would OP want as the desired result?  It was already pointed out by clawson that an ISR isn't instantaneous so why not do a simple flag check in the mainline code?  So "solution" #1.

 

Now, the condition OP's snippet addresses is a pin change.  So the solution is to latch the flag.  Inline checking may well lose that.

 

Actually, I lied about solution #1, as that probably is "why not just take the C solution?"  Again, why is that solution bad, and what does OP hope to gain?  Let's just say these two are #1 and #2.

 

Do we know which model of AVR is involved?  It will make a difference with the "optimal" solution coming up...

 

So, to do it "right" one really needs to study and understand the AVR instruction set, and which instructions affect flag registers.  That said, "solution" #3 is to use a GPIOR0 bit to latch the condition.  [GPIOR0 is in reach of I/O bit manipulation functions on many/most AVR8 models.  There may be other bits that could be used, as has been discussed over the years.]  Remember to clear the latch when tested/used.

 

If you really want to save the last half-dozen cycles of latency then use a simpler interrupt latch that doesn't require conditioning, and put the latch and RETI in the vector table entry. 

 

[edit]   As many here know, I used the CodeVisionAVR C compiler for many years, so thinking in terms of bit variables and register variables and GPIOR has become ingrained.  All my discussion above is contained in the source code such as:

#include <io.h>

bit mylatch;

// Pin change 0-7 interrupt service routine
interrupt [PC_INT0] void pin_change_isr0(void)
{
    if (PINB & (1<<PINB3))
        {
        mylatch = 1;
        }
} 
void main(void)
{
    while (1) 
    { 
    }
    
}
                 	.CSEG
                 _pin_change_isr0:
                 ; .FSTART _pin_change_isr0
                 ; 0000 0008     if (PINB & (1<<PINB3))
00002a 99b3      	SBIC 0x16,3
                 ; 0000 0009         {
                 ; 0000 000A         mylatch = 1;
00002b 9a88      	SBI  0x11,0
                 ; 0000 000B         }
                 ; 0000 000C }
00002c 9518      	RETI
                 ; .FEND

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Tue. Nov 16, 2021 - 04:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

But as the compiler's code shows you don't set the flag with LDI alone (*unless* you have bound it to a reserved register (which g_interrupt is not)) 

 

Hmmmm... again, my code works as-is, but I really need to examine the assembly code to understand why.  Thanks for the advice.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

bradk3 wrote:
  The only difficulty I can see is I don't understand how to reference a C variable (my "g_interrupt" flag) in a separate assembly file, but I should be able to figure that out.
If there's ever anything in the assembler you don't know ask C how to do it. Simply write (just a light framework) of what you want to do in C, compile it with the -save-temps option, look at the .s file that generates.

 

 

...

 

Bottom line: just reference "g_interrupt" in your .S and it will be found (unlike C there is no concept of "type" in Asm, everything is just the address of something so you don't need an "extern" declaration as it's just an "unknown name" that will be found by the linker).

 

Thank you so much for taking the time to go over all that.  That's all valuable to know.  Regardless of how I proceed with this project, I'll spend some time examining what the compiler puts out.

 

I really appreciate the response!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

bradk3 wrote:
For speed reasons, I want to code a Pin Change interrupt in inline assembler.  The idea is to modify a global flag when an interrupt occurs that can then be checked and handled by the main code. 

Respondents indeed spoke of the magic incantations and eye-of-newt needed.  And went on to suggest a separate source file as a better/suggested approach.

 

But let's take note of the elephant in the room-- setting aside the source mechanics for a moment, what sequence would OP want as the desired result?  It was already pointed out by clawson that an ISR isn't instantaneous so why not do a simple flag check in the mainline code?  So "solution" #1.

 

Now, the condition OP's snippet addresses is a pin change.  So the solution is to latch the flag.  Inline checking may well lose that.

 

Actually, I lied about solution #1, as that probably is "why not just take the C solution?"  Again, why is that solution bad, and what does OP hope to gain?  Let's just say these two are #1 and #2.

 

Do we know which model of AVR is involved?  It will make a difference with the "optimal" solution coming up...

 

So, to do it "right" one really needs to study and understand the AVR instruction set, and which instructions affect flag registers.  That said, "solution" #3 is to use a GPIOR0 bit to latch the condition.  [GPIOR0 is in reach of I/O bit manipulation functions on many/most AVR8 models.  There may be other bits that could be used, as has been discussed over the years.]  Remember to clear the latch when tested/used.

 

If you really want to save the last half-dozen cycles of latency then use a simpler interrupt latch that doesn't require conditioning, and put the latch and RETI in the vector table entry. 

 

This... is a lot to unpack, but I want to understand it.  I'm not sure what you're referring to by "a GPIOR0 bit", but I'll do some research.  Thank you for pointing me to something new to learn!  I appreciate it.

 

Ultimately, I'm hoping to do more with this ISR.  The goal is to implement a simple one-wire protocol between two AVRs (short pulses = "0", long pulses = "1"), so the ISR will need to detect which type of pulse is received and assemble the bits into complete bytes.  I need a tight ISR because the ISR setup is taking long enough that I end up not being able to differentiate between the pulses without slowing down my transmission rate more than I hope to.  

 

I'm working with an attiny85, in case that makes a difference.  I should have made that clearer.

Last Edited: Tue. Nov 16, 2021 - 04:52 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

bradk3 wrote:
My concern is that I'm declaring this ISR "naked" and my understanding is that doing that shifts a lot more of the responsibility on me.

AIUI, It shifts the entire responsibility on to you:

 

https://gcc.gnu.org/onlinedocs/gcc/AVR-Function-Attributes.html

 

Again, either do it in true C, or do it in true assembler - why mess with some half-baked mish-mash?

 

Yup.  This is exactly what I needed to hear.  It's the inline assembly that has really been throwing me off.  Thank you!

 

awneil wrote:

bradk3 wrote:
The only difficulty I can see is I don't understand how to reference a C variable (my "g_interrupt" flag) in a separate assembly file, but I should be able to figure that out.

I'd look at the compiler-generated assembler - the compiler knows how to do it ...

 

Agreed.  I need to spend some quality time looking over the compiler-generated assembly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


bradk3 wrote:
I'm not sure what you're referring to by "a GPIOR0 bit",
This:

 

 

Now the tin85 seems to do a spectacularly poor job of explaining these (there is no other reference to "GPIOR0" anywhere in the datasheet and the page 10 that links to is the wrong thing!!) but the point is that these are three "spare" SFR registers. Each has 8 bits that are fully read/write and latched. So anything you write there just stays there but the key thing is where they are located in the memory map. They are at IO addresses 0x11, 0x12, 0x13 which are all within the 0x00 .. 0x1F range of SBI/CBI/SBIC/SBIS.

 

So if in your C code you forget "g_interrupt" and just have the ISR do:

ISR(PCINT0_vect) {
    if (PINB & (1 << IO_PIN)) {
        GPIOR0 |= (1 << 5); // pick your bit 0.. 7 !
    }
}

then in the main() loop:

        if (GPIOR0 & (1 << 5))
        {
            PORTB ^= (1<<LED_PIN);
        }

what you get is (ISR):

    if (PINB & (1 << IO_PIN)) {
  66:	b3 99       	sbic	0x16, 3	; 22
        GPIOR0 |= (1 << 5);
  68:	8d 9a       	sbi	0x11, 5	; 17
    }

and in the main() code:

        if (GPIOR0 & (1 << 5))
  50:	8d 9b       	sbis	0x11, 5	; 17
  52:	fe cf       	rjmp	.-4      	; 0x50 <main+0x10>

which is about as tight as you can possibly get this.

 

So you can have 24 separate "bit variables" that can all be read and written using the tightest opcodes possible.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

bradk3 wrote:
The goal is to implement a simple one-wire protocol between two AVRs (short pulses = "0", long pulses = "1"), so the ISR will need to detect which type of pulse is received and assemble the bits into complete bytes.  I need a tight ISR because the ISR setup is taking long enough that I end up not being able to differentiate between the pulses without slowing down my transmission rate more than I hope to.  

 

do you have this working at a slower speed?  if not, why not....  That would seem to me, the first goal, then optimize once it's working solid at the slower speed....

jim

 

FF = PI > S.E.T

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:
  The goal is to implement a simple one-wire protocol between two AVRs 

as noted earlier, are interrupts really the best way to do this? would it not be better to just run in a tight loop?

 

With careful design of your protocol, you could make it so that the timing is only critical within each bit - and not between bits - so you wouldn't have to spend long periods in this loop with interrupts locked-out.

 

The Maxim (formerly Dallas) 1-WireTM protocol works this way and, I think, so do the WS2812 (and similar) LEDs?

 

Another possibility might be to "synthesise" it using the UART or SPI peripheral ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

So if in your C code you forget "g_interrupt" and just have the ISR do:

ISR(PCINT0_vect) {
    if (PINB & (1 << IO_PIN)) {
        GPIOR0 |= (1 << 5); // pick your bit 0.. 7 !
    }
}

then in the main() loop:

        if (GPIOR0 & (1 << 5))
        {
            PORTB ^= (1<<LED_PIN);
        }

what you get is (ISR):

    if (PINB & (1 << IO_PIN)) {
  66:	b3 99       	sbic	0x16, 3	; 22
        GPIOR0 |= (1 << 5);
  68:	8d 9a       	sbi	0x11, 5	; 17
    }

and in the main() code:

        if (GPIOR0 & (1 << 5))
  50:	8d 9b       	sbis	0x11, 5	; 17
  52:	fe cf       	rjmp	.-4      	; 0x50 <main+0x10>

which is about as tight as you can possibly get this.

 

So you can have 24 separate "bit variables" that can all be read and written using the tightest opcodes possible.

 

Ah!  That makes complete sense!  So good to know.  I'm probably going to use this, since it avoids a lot of the issues I'm trying to avoid.  If I go all-in and insert this directly into the interrupt vector table, I really can't complain about the delay at all.

 

I've seen similar approaches using unused peripheral registers for general-purpose storage.  I like this better.

 

Thank you!

Last Edited: Tue. Nov 16, 2021 - 06:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

bradk3 wrote:
The goal is to implement a simple one-wire protocol between two AVRs (short pulses = "0", long pulses = "1"), so the ISR will need to detect which type of pulse is received and assemble the bits into complete bytes.  I need a tight ISR because the ISR setup is taking long enough that I end up not being able to differentiate between the pulses without slowing down my transmission rate more than I hope to.  

 

do you have this working at a slower speed?  if not, why not....  That would seem to me, the first goal, then optimize once it's working solid at the slower speed....

jim

 

Yes, using straight C and generous delays in my protocol, I've been able to get an initial stab at this protocol working.  My hope now is to speed it up.

 

Still, it's solid advice that I often forget.  Thank you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

bradk3 wrote:
  The goal is to implement a simple one-wire protocol between two AVRs 

as noted earlier, are interrupts really the best way to do this? would it not be better to just run in a tight loop?

 

With careful design of your protocol, you could make it so that the timing is only critical within each bit - and not between bits - so you wouldn't have to spend long periods in this loop with interrupts locked-out.

 

The Maxim (formerly Dallas) 1-WireTM protocol works this way and, I think, so do the WS2812 (and similar) LEDs?

 

Another possibility might be to "synthesise" it using the UART or SPI peripheral ...

 

The WS2812 protocol is exactly what I'm attempting to duplicate.  Generating code to send data using this protocol has been pretty simple.  Receiving it has been more difficult for me.  

 

I've looked at the 1-Wire protocol, but it doesn't seem to suit my needs.  I want to be able to send a string of data down a serial line of attiny85s, have each one strip off the information it needs and send the rest up the chain - just like the WS2812s.  I also think I can make it bidirectional, if I'm careful.  We'll see.

 

As with everyone else, I really appreciate you taking the time to give me more ideas.  It may be better to avoid interrupts altogether and look at polling the pin or attempting something with the SPI.

 

Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:
Generating code to send data using this protocol has been pretty simple.  Receiving it has been more difficult

Yes, that is generally the case: with sending, you're in control of what you do & when; for receiving, you're at the whim of the sender, and have to be able to "drop everything" and jump to it when a message starts to arrive - irrespective of what else you might be doing at the time.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Why not simply steal the WS2812 support from Arduino? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess that just does the sending part ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If reading is required I'd have thought they'd have a complete implementation too? (dunno, haven't looked, but let's face there's a lot of Arduino driven WS stuff out in the world) 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
dunno, haven't looked

likewise.

 

but let's face there's a lot of Arduino driven WS stuff out in the world

indeed, but I get the impression that it's mostly just driving the LEDs - seldom (if ever?)  receiving ?

 

Gave up after 5th page of https://www.google.com/search?q=WS2812+receiver - all "receivers" mentioned were receiving some other kind of control, and just driving the LEDs

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

clawson wrote:
dunno, haven't looked

likewise.

 

but let's face there's a lot of Arduino driven WS stuff out in the world

indeed, but I get the impression that it's mostly just driving the LEDs - seldom (if ever?)  receiving ?

 

Gave up after 5th page of https://www.google.com/search?q=WS2812+receiver - all "receivers" mentioned were receiving some other kind of control, and just driving the LEDs

 

Yeah, I've looked as well.  Everything is focused on using the protocol to drive the WS2812 LEDS.  I haven't seen any demand for receiving the protocol.  It's too bad, because it seems like an ingenious method for transmitting to chained devices using a single transmission wire.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:
ingenious method for transmitting to chained devices using a single transmission wire.

Well they do also have power and gnd, so technically its three wires, but whos counting....

If you used a more modern tiny, ie.AVR0/1, one with a real usart, the hardware would do all the heavy lifting for you using a half duplex protocol on a single wire or better a diff 485....

 

 

FF = PI > S.E.T

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

bradk3 wrote:
ingenious method for transmitting to chained devices using a single transmission wire.

Well they do also have power and gnd, so technically its three wires, but whos counting....

If you used a more modern tiny, ie.AVR0/1, one with a real usart, the hardware would do all the heavy lifting for you using a half duplex protocol on a single wire or better a diff 485....

 

 

Upgrade from the attiny85?!  Surely you jest.

 

It is tempting, though.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

if you AVR has a timer with input capture, I'd be looking at using that - it will help measure the bit time for you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The AVR LIB has several examples:

 

Under "Manually defined ISRs" on https://www.nongnu.org/avr-libc/...

it says: 

Another solution is to still implement the ISR in C language but take over the compiler's job of generating the prologue and epilogue. This can be done using the ISR_NAKED attribute to the ISR() macro. Note that the compiler does not generate anything as prologue or epilogue, so the final reti() must be provided by the actual implementation. SREG must be manually saved if the ISR code modifies it, and the compiler-implied assumption of __zero_reg__ always being 0 could be wrong (e. g. when interrupting right after of a MUL instruction).

 

ISR(TIMER1_OVF_vect, ISR_NAKED) {
    PORTB |= _BV(0); // results in SBI which does not affect SREG
    reti();
}

 

And the C + ASM example here https://www.nongnu.org/avr-libc/... has a "isrs.S" file that shows which registers to save.

 

Mike

When you're used to privilege, equality feels like oppression. / Malena Ernman

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The example just ignores the GCC documentation which clearly states this is not supported (which does not imply it won't work sometimes, though).

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bradk3 wrote:

ingenious method for transmitting to chained devices using a single transmission wire.

ki0bk wrote:
Well they do also have power and gnd, so technically its three wires, but who's counting....

Similarly for the Maxim/Dallas "1-WireTM"...

 

with a real usart, the hardware would do all the heavy lifting for you

+1

 

So the UART RX receives from the previous node in the chain, and the UART TX sends to the next node in the chain:

           UART              UART              UART   
        +--------+        +--------+        +--------+
        |        |        |        |        |        |
------->+ RX  TX +------->+ RX  TX +------->+ RX  TX +------->
        |        |        |        |        |        |
        +--------+        +--------+        +--------+

Could similarly be done with SPI...

 

 

half duplex

are we talking duplex at all - isn't this just a simplex (one-way) protocol ... ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

are we talking duplex at all - isn't this just a simplex (one-way) protocol ... ?

 

With my application, I think I can implement a simple handshaking method to allow bi-directional communication.  The trick is that, for my purposes, I can know where a message will be coming from (upstream vs downstream) but not when.  

 

I think...  Considering I have yet to make it work, I may be overly optimistic.

Last Edited: Wed. Nov 17, 2021 - 04:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have to thank everyone again for your input.  I've learned a lot and changed how I'm approaching this problem because of the discussion, tips, and pointers that I've received.  

 

I really appreciate it.  It's people like you that make things like this accessible.

 

Cheers!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I think was noted or as least implied earlier,

if OP's ISR is just to set a bit,

OP likely does not need an ISR at all,

just the interrupt flag.

 

As clawson noted, If a flag-setting ISR is needed,

a bit of GPIOR0 is ideal.

The ISR might not need to save any registers.

 

IIRC there was a thread about driving ws2812s some time ago.

Nothing about reading them.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
IIRC there was a thread about driving ws2812s some time ago.

indeed - there have been several

 

Nothing about reading them.

indeed.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which speed do you need to send data, over how long a distance, what else does the tiny85 need to do (and how time critical is it)

others thing we need to know do you want to run the internal osc. or a crystal, (if internal do you want to calibrate the speed).

 

I have a hard time to see why you need real high speed of data to/from a tiny85.

If you use the USI something like 1 MByte is possible and 100 KByte a sec is easy. (even with 5% error in speed), but only if there not are other jobs that are time critical.