fastest way to flip a bit in I/O port, atmega328, using C?

Thread Starter

mikewax

Joined Apr 11, 2016
230
EDIT: There is a complement bit instruction on some processors, but apparently not on this one. I've been writing code in C so long I sometimes forget what's under the hood. If writing 1 to the PINB register will toggle the output then that probably as good as you're going to be able to do. Don't forget that the instruction fetch is overlapped with the execution of the previous instruction so you have to take that into account.

I apologize for the misdirection.
so even in assembly it's still gonna take two cycles?
 

Thread Starter

mikewax

Joined Apr 11, 2016
230
SBI & CBI instructions both take 2 clock cycles, which directly manipulate the bit in an I\O port.
OK maybe i'm over my head here, but i was looking in the data sheet and there's this instruction that takes only 1 cycle. the data sheet says:

instruction: OUT
operands: P, Rr (i'm assuming that this refers to port and core registers)
description: Out Port
operation: P <- Rr
flags: None
cycles: 1

so if i read this right, it means that i could load register Rx with literal A, and register Ry with literal B, then i could do an inline code something like this:
asm (
"ldi r20, A \n" //load register r20 with A
"ldi r21, B \n" //load register r21 with B
: : : "r20","r21"
);

then i could do
asm ( "out B, r30 \n" );
and
asm ( "out B, r31 \n" );

is this possible? can i use these core registers?
 
Last edited:

Thread Starter

mikewax

Joined Apr 11, 2016
230
OK maybe i'm over my head here, but i was looking in the data sheet and there's this instruction that takes only 1 cycle. the data sheet says:

is this possible? can i use these core registers?
OK i'm answering my own question and yes i used the OUT instruction and got the execution time down to 80ns
 

dannyf

Joined Sep 13, 2015
2,197
The best solution to your problem has already been provided on a silver platter, as evidenced by a quick read of the data sheet. You just cannot recognize it yet, :)
 

Thread Starter

mikewax

Joined Apr 11, 2016
230
The best solution to your problem has already been provided on a silver platter, as evidenced by a quick read of the data sheet. You just cannot recognize it yet, :)
yes that's true. i just didn't know that i could do assembly instructions with C code.
the strange thing is that i keep measuring 80ns and according to the data sheet it should be 62.5ns.
although it doesn't really matter since the problem is solved.
 

djsfantasi

Joined Apr 11, 2010
9,237
yes that's true. i just didn't know that i could do assembly instructions with C code.
the strange thing is that i keep measuring 80ns and according to the data sheet it should be 62.5ns.
although it doesn't really matter since the problem is solved.
Your last post is #33. I mentioned using inline assembler in post #11.
 

dannyf

Joined Sep 13, 2015
2,197
keep measuring 80ns and according to the data sheet it should be 62.5ns.
Without knowing your clock speed and how you are measuring it, it is hard to tell with those numbers are reasonable. For example the 80ns number could simply be due to some loop overhead.

As to the use of the out instruction on two registers, you have to make sure that other routines aren't using them or you have to push and pop those registers.

They also have the side effect of impacting other pins on the port. Which may or may not be a problem.

Generally the PINx register approach is the fastest. But sometimes using the pwm module can produce code with optimal code as well.
 

Thread Starter

mikewax

Joined Apr 11, 2016
230
Without knowing your clock speed and how you are measuring it, it is hard to tell with those numbers are reasonable. For example the 80ns number could simply be due to some loop overhead.
As to the use of the out instruction on two registers, you have to make sure that other routines aren't using them or you have to push and pop those registers.
They also have the side effect of impacting other pins on the port. Which may or may not be a problem.
Generally the PINx register approach is the fastest. But sometimes using the pwm module can produce code with optimal code as well.
well i thought that at 16MHz i would measure 62.5ns. but yeah it probably is the loop overhead.
as for using the core registers, i don't have to worry about it because there's a directive that i specify in the code so that gcc will save me those registers. the code looks like this:
Code:
asm (
"ldi r20, 0 \n" //load register r20 with 0
"ldi r21, 16 \n" //load register r21 with 1
: : : "r20","r21" //compiler directive to reserve the two registers
);
and this:
Code:
ISR(TIMER1_OVF_vect){ asm ( "out 0x05, r21 \n" ); }// Timer1 overflow interrupt: lower pin B0
ISR(TIMER1_COMPA_vect){ asm ( "out 0x05, r20 \n" ); }// Timer1 compare match interrupt: raise pin B0
so it's simple as can be.
 

dannyf

Joined Sep 13, 2015
2,197
somebody said that it wouldn't work on the Mega8's.
likely an oversight in the datsheet, as other avrs in the same family have it.

unless someone can show that writing to pinx doesn't work on an actual atmega8, i would assume that it works.

btw, writing pinx is widely known - it is in the datasheet.
 

Thread Starter

mikewax

Joined Apr 11, 2016
230
the interrupt approach will be considerably slower due to interrupt overhead.
well there's two different points here. first, which is faster.
this: asm ( "out 0x05, r21 \n" )
or this: PINB |= x

by my measurement, the out instruction was 80ns and the PINB was 120ns.
either way, using an interrupt is the only way i know to do it. the whole problem arose when i discovered that pin B1 on the atmega doesn't work. the signal coming out of that pin was out of control. but the PWM circuitry behind it was still working. so i set up an interrupt to put the pwm signal on another pin. the only one that worked was B4, so that's what i did.
 

Thread Starter

mikewax

Joined Apr 11, 2016
230
neither.
here is an atmega8 running at 16MIPS and compiled at -O3.
i'm not following. your saying that both of them compiled down to the same effect?
i'm using the Arduino 1.6.12 IDE and it doesn't seem to provide any compiling options. i would way rather use the PINx command if i could.
i just wrote a timer routine with a nested FOR loop that basically said "flip the pin 10 million times" and used a stopwatch.
and what software is that you're using? i'd like to have that.
 
Top