EEVblog Electronics Community Forum

Electronics => Microcontrollers => Topic started by: chrome on August 03, 2012, 03:16:22 pm

Title: AVR GCC Set multiple bits
Post by: chrome on August 03, 2012, 03:16:22 pm
What is the best/fastest/easiest way to set a number of bits in a byte

Here is the scenario, I'm driving a 7segment display but the A trough F lines are not on the same register (they are spread over portB and portD on an attiny2313.

I have 2 arrays for the corresponding values to outputs and a mask value so it can know what value to take from these arrays
Code: [Select]
unsigned char digit_output_B[10] = {0x79,0x30,0x5a,0x7a,0x33,0x6b,0x6b,0x38,0x7b,0x7b}; //array for displaying of digits
unsigned char digit_output_D[10] = {0x02,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x02,0x00}; //array for displaying of digits
unsigned char portB_mask = 0b01111011; //PB6,5,4,3,1,0
unsigned char portD_mask = 0b00000010; //PD1

Now the problems is there are other in and outputs on these ports so I can't change any of the other bits but I'm really at a loss here for how to do it.

Anybody know of any good ways?
Title: Re: AVR GCC Set multiple bits
Post by: TerminalJack505 on August 03, 2012, 03:43:17 pm
Without giving it a whole lot of thought (in other words I didn't test this), something like this, perhaps?

Code: [Select]
PORTB = (PORTB & ~portB_mask[i]) | digit_output_B[i];

You can speed up the operation by storing the mask inverted.  This leads to...

Code: [Select]
PORTB = (PORTB & portB_mask[i]) | digit_output_B[i];
Title: Re: AVR GCC Set multiple bits
Post by: chrome on August 03, 2012, 03:55:29 pm
I was actually thinking of basically that but using way more steps, but yeah i think that's gonna work.
Thanks
Title: Re: AVR GCC Set multiple bits
Post by: ultrageek.lloyd on August 07, 2012, 08:31:13 am
Without giving it a whole lot of thought (in other words I didn't test this), something like this, perhaps?

Code: [Select]
PORTB = (PORTB & ~portB_mask[i]) | digit_output_B[i];

You can speed up the operation by storing the mask inverted.  This leads to...

Code: [Select]
PORTB = (PORTB & portB_mask[i]) | digit_output_B[i];

shouldn't that be
Code: [Select]
PORTB |= digit_output_B[i] & portB_mask;
PORTD |= digit_output_D[i] & portD_mask;
as i read it the mask is to determine what bits from the array value are to be put in PORTB and which to be put in PORTD
Title: Re: AVR GCC Set multiple bits
Post by: nctnico on August 07, 2012, 10:43:30 am
One thing to be aware off is that with some microcontroller the value read is the state of the pin and not the output register. If the output is set to open drain then you can get unexpected results.
Title: Re: AVR GCC Set multiple bits
Post by: TerminalJack505 on August 07, 2012, 11:13:26 am

shouldn't that be
Code: [Select]
PORTB |= digit_output_B[i] & portB_mask;
PORTD |= digit_output_D[i] & portD_mask;
as i read it the mask is to determine what bits from the array value are to be put in PORTB and which to be put in PORTD

I think that will only set bits but won't clear them.  Apply your code to digit_output_D[1], for example.  I think the OP's intention in this case is for bit 1 to be cleared. 

Your code would be saying:

Code: [Select]
PORTD |= 0x00 & 0b00000010;
Which would leave the port's value unchanged.

You did allude to an error in my original code, however.  I said

Code: [Select]
portB_mask[i]
but should have said

Code: [Select]
portB_mask
Title: Re: AVR GCC Set multiple bits
Post by: hlavac on August 31, 2012, 04:20:27 pm
One thing to be aware off is that with some microcontroller the value read is the state of the pin and not the output register. If the output is set to open drain then you can get unexpected results.

Not with AVR, it has separate registers for output drivers/pullup enables (PORTx) and actual inputs (PINx) and thus avoids this problem nicely.
Title: Re: AVR GCC Set multiple bits
Post by: jeremy on September 09, 2012, 09:44:49 am
can you define what fastest means? aka


it is important what optimisation settings you are using as well; not all avr instructions are exclusively single cycle (for example, branching).
Title: Re: AVR GCC Set multiple bits
Post by: Psi on September 09, 2012, 10:19:37 am
When faced with a similar problem (though not exactly the same as yours) here is what i did.

Is it the fastest code - no
Was it fast enough - yes

void LEDColumnUpdate(uint8_t led1, uint8_t led2)
{
    // led1 and led2 are a number 0-31 to set which of the 5 leds on a column are active.
   PORTB = ((led1 & 0b00000001) << 4)  | ((led1 & 0b00000100) << 3) ;

   PORTE =     ((led2 & 0b00000010) >> 1)  | ((led2 & 0b00000001) << 1)
                   | ((led2 & 0b00001000) >> 1)  | ((led2 & 0b00000100) << 1)
                   |  (led2 & 0b00010000)            | ((led1 & 0b00001000) << 2)
                   | ((led1 & 0b00010000) << 2)  | ((led1 & 0b00000010) << 6);   
}

Basically it masks out a specific bit in the variable and shifts it right or left to match up with the bit in the PORT register were that column is physically connected. Then all of these are OR'ed together.

I actually had 5 leds, not 2 as shown here, i removed the others from the function before posting to make it easier to read. Some of the other leds were on ports used for other things and needed special handling to insure no interrupt happened while they were updating.

When the project was done the 8mhz ATMega micro was
- Multiplexing 5x 5x7 led blocks with 5 level PWM brightness control (fast enough that it didn't flicker)
- Doing the math for led animations and scrolling messages
- Performing some software signal filtering and syncing to 50Hz light pulses from a phototransistor in order to capture data in the pulses.
- Scanning 3 pushbuttons with software debounce
And it wasn't showing any signs of speed issues

(http://psi.abcom.co.nz/tags2.jpg)
The reason you can only see 4 led blocks is because the bigger top 5x7 one is bicolor red/green, from the codes point of view it's addressed like 2 separate 5x7 blocks.
Title: Re: AVR GCC Set multiple bits
Post by: andersm on September 09, 2012, 12:39:07 pm
Basically it masks out a specific bit in the variable and shifts it right or left to match up with the bit in the PORT register were that column is physically connected. Then all of these are OR'ed together.
I usually do this kind of bit shuffling using lookup tables. It's faster, and depending on the number of bits can be smaller too. And then I have a stern word with whoever laid out the board to also consider how the processor will be used instead.
Title: Re: AVR GCC Set multiple bits
Post by: Psi on September 09, 2012, 12:52:19 pm
And then I have a stern word with whoever laid out the board to also consider how the processor will be used instead.

hehe yeah,

One thing people should always keep in mind when designing a pcb is, if you have to split a byte/word across multiple ports it's really useful to keep it in order ie.. bits 0123 on one port and 4567 on another.
That way when you join them back up you can mask,copy and shift the entire block of 4 instead of one bit at a time.
Title: Re: AVR GCC Set multiple bits
Post by: adam1213 on September 09, 2012, 02:13:55 pm
A nice way of doing it is to use unions
Title: Re: AVR GCC Set multiple bits
Post by: phil_jp1 on September 11, 2012, 01:25:20 am
A nice way of doing it is to use unions

In critical systems unions should be used with caution, or not used at all. But for single platform with simple software to blink some leds or to control a display - I don't see the problem.
Title: Re: AVR GCC Set multiple bits
Post by: Psi on September 11, 2012, 08:08:36 am
I should really learn unions.
Do they produce the same code in the background or utilize hardware features that results in quicker code to if you did it yourself?
Title: Re: AVR GCC Set multiple bits
Post by: phil_jp1 on September 11, 2012, 10:13:24 am
Do they produce the same code in the background or utilize hardware features that results in quicker code to if you did it yourself?
There is no special hardware features. How efficient code would be from using unions depends on a compiler. I highly doubt that they would be more efficient than your hand-written code, but I haven't checked though. If anything, they would be easier to use. More elegant C code.
Title: Re: AVR GCC Set multiple bits
Post by: ejeffrey on September 19, 2012, 01:42:56 pm
I should really learn unions.
Do they produce the same code in the background or utilize hardware features that results in quicker code to if you did it yourself?

Unions shouldn't be any more or less efficient than just manually doing the same thing.  However, the C standard does not actually require unions to do anything particularly useful.  In particular, the alignment of the elements is not guaranteed to do what you want, and if you write to one element and read from a different element, the compiler is allowed to do anything it damn well pleases.  For instance, if you create a union of a 32 bit dword and the 16 bit high and low words, writing to the low word is not guaranteed to be reflected in a subsequent reading of the whole 32 bit value.  Specific ABIs and compilers will guarantee that the union operates the way you want (generally requiring the volatile keyword), but it is certainly not portable especially across machines with different word sizes, alignment requirements, and byte order.

As phil_jp1 said, it is OK to use unions for low-level code that is targeted to a specific machine architecture as the ABI will generally guarantee that unions work the way you want.  However, I still prefer to avoid unions in favor of bit arithmetic, (carefully written) macros, and inline functions.