AVR GCC Set multiple bits - Page 1
Forum section: electronics / general

author: chrome
author_rank: Regular Contributor
timestamp: August 03, 2012, 03:16:22 pm
content: What is the best/fastest/easiest way to set a number of bits in a byteHere 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 arraysCode:unsigned chardigit_output_B={0x79,0x30,0x5a,0x7a,0x33,0x6b,0x6b,0x38,0x7b,0x7b};//array for displaying of digitsunsigned chardigit_output_D={0x02,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x02,0x00};//array for displaying of digitsunsigned charportB_mask=0b01111011;//PB6,5,4,3,1,0unsigned charportD_mask=0b00000010;//PD1Now 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?

author: TerminalJack505
author_rank: Super Contributor
timestamp: August 03, 2012, 03:43:17 pm
content: Without giving it a whole lot of thought (in other words I didn't test this), something like this, perhaps?Code:PORTB = (PORTB & ~portB_mask) | digit_output_B;You can speed up the operation by storing the mask inverted. This leads to...Code:PORTB = (PORTB & portB_mask) | digit_output_B;

author: chrome
author_rank: Regular Contributor
timestamp: August 03, 2012, 03:55:29 pm
content: I was actually thinking of basically that but using way more steps, but yeah i think that's gonna work.Thanks

author: ultrageek.lloyd
author_rank: Contributor
timestamp: August 07, 2012, 08:31:13 am
content: Quote from: TerminalJack505 on August 03, 2012, 03:43:17 pmshouldn't that beCode:PORTB |= digit_output_B & portB_mask;PORTD |= digit_output_D & 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

author: nctnico
author_rank: Super Contributor
timestamp: August 07, 2012, 10:43:30 am
content: 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.

author: TerminalJack505
author_rank: Super Contributor
timestamp: August 07, 2012, 11:13:26 am
content: Quote from: ultrageek.lloyd on August 07, 2012, 08:31:13 amI think that will only set bits but won't clear them. Apply your code to digit_output_D, for example. I think the OP's intention in this case is for bit 1 to be cleared.Your code would be saying:Code:PORTD |= 0x00 & 0b00000010;Which would leave the port's value unchanged.You did allude to an error in my original code, however. I saidCode:portB_maskbut should have saidCode:portB_mask

author: hlavac
author_rank: Frequent Contributor
timestamp: August 31, 2012, 04:20:27 pm
content: Quote from: nctnico on August 07, 2012, 10:43:30 amNot with AVR, it has separate registers for output drivers/pullup enables (PORTx) and actual inputs (PINx) and thus avoids this problem nicely.

author: jeremy
author_rank: Super Contributor
timestamp: September 09, 2012, 09:44:49 am
content: can you define what fastest means? akaminimum lines of codeminimum instructionsfastest switching timeetcit is important what optimisation settings you are using as well; not all avr instructions are exclusively single cycle (for example, branching).

author: Psi
author_rank: Super Contributor
timestamp: September 09, 2012, 10:19:37 am
content: When faced with a similar problem (though not exactly the same as yours) here is what i did.Is it the fastest code - noWas it fast enough - yesvoid 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 debounceAnd it wasn't showing any signs of speed issuesThe 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.

author: andersm
author_rank: Super Contributor
timestamp: September 09, 2012, 12:39:07 pm
content: Quote from: Psi on September 09, 2012, 10:19:37 amI 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.

author: Psi
author_rank: Super Contributor
timestamp: September 09, 2012, 12:52:19 pm
content: Quote from: andersm on September 09, 2012, 12:39:07 pmhehe 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.

author: adam1213
author_rank: Regular Contributor
timestamp: September 09, 2012, 02:13:55 pm
content: A nice way of doing it is to use unions

author: phil_jp1
author_rank: Regular Contributor
timestamp: September 11, 2012, 01:25:20 am
content: Quote from: adam1213 on September 09, 2012, 02:13:55 pmIn 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.

author: Psi
author_rank: Super Contributor
timestamp: September 11, 2012, 08:08:36 am
content: 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?

author: phil_jp1
author_rank: Regular Contributor
timestamp: September 11, 2012, 10:13:24 am
content: Quote from: Psi on September 11, 2012, 08:08:36 amThere 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.

author: ejeffrey
author_rank: Super Contributor
timestamp: September 19, 2012, 01:42:56 pm
content: Quote from: Psi on September 11, 2012, 08:08:36 amUnions 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.