Author Topic: AVR GCC Set multiple bits  (Read 8428 times)

0 Members and 1 Guest are viewing this topic.

Offline chrome

  • Regular Contributor
  • *
  • Posts: 189
  • Country: be
AVR GCC Set multiple bits
« 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?
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1306
  • Country: 00
Re: AVR GCC Set multiple bits
« Reply #1 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];
 

Offline chrome

  • Regular Contributor
  • *
  • Posts: 189
  • Country: be
Re: AVR GCC Set multiple bits
« Reply #2 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
 

Offline ultrageek.lloyd

  • Contributor
  • Posts: 12
Re: AVR GCC Set multiple bits
« Reply #3 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
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 18952
  • Country: nl
    • NCT Developments
Re: AVR GCC Set multiple bits
« Reply #4 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.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1306
  • Country: 00
Re: AVR GCC Set multiple bits
« Reply #5 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
« Last Edit: August 07, 2012, 11:23:26 am by TerminalJack505 »
 

Offline hlavac

  • Frequent Contributor
  • **
  • Posts: 534
  • Country: cz
Re: AVR GCC Set multiple bits
« Reply #6 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.
Good enough is the enemy of the best.
 

Offline jeremy

  • Frequent Contributor
  • **
  • Posts: 936
  • Country: au
Re: AVR GCC Set multiple bits
« Reply #7 on: September 09, 2012, 09:44:49 am »
can you define what fastest means? aka

  • minimum lines of code
  • minimum instructions
  • fastest switching time
  • etc

it is important what optimisation settings you are using as well; not all avr instructions are exclusively single cycle (for example, branching).
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 7526
  • Country: nz
Re: AVR GCC Set multiple bits
« Reply #8 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


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.
« Last Edit: September 09, 2012, 12:54:17 pm by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1120
  • Country: fi
Re: AVR GCC Set multiple bits
« Reply #9 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.

Offline Psi

  • Super Contributor
  • ***
  • Posts: 7526
  • Country: nz
Re: AVR GCC Set multiple bits
« Reply #10 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.
« Last Edit: September 09, 2012, 12:55:41 pm by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline adam1213

  • Regular Contributor
  • *
  • Posts: 119
  • Country: au
Re: AVR GCC Set multiple bits
« Reply #11 on: September 09, 2012, 02:13:55 pm »
A nice way of doing it is to use unions
 

Offline phil_jp1

  • Regular Contributor
  • *
  • Posts: 103
  • Country: ua
Re: AVR GCC Set multiple bits
« Reply #12 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.
http://JumperOne.com - Electronic projects, tutorials, hacks, etc.
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 7526
  • Country: nz
Re: AVR GCC Set multiple bits
« Reply #13 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?
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline phil_jp1

  • Regular Contributor
  • *
  • Posts: 103
  • Country: ua
Re: AVR GCC Set multiple bits
« Reply #14 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.
http://JumperOne.com - Electronic projects, tutorials, hacks, etc.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 2054
  • Country: us
Re: AVR GCC Set multiple bits
« Reply #15 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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf