Trying to understand bitmath by using ATtiny 10

Hey guys and gals

I really want to try to learn how to manipulate registers so I can get better speed and expand my knowledge of MCU’s. I am not an avid programmer, so this is all uncharted territory for me.

Manipulating registers requires a lot of bit math, and I am trying to practice it. I thought a fun way to learn would be by using one of the simplest MCU’s, the ATtiny 10. My goal for the project is to have it read an analog voltage, and if it is below a threshold, turn the output off. It will also read a switch and if the switch is high it will output a 75% duty cycle PWM signal on output. If switch is low it will output a 25% duty cycle PWM signal on output. (frequency can be very low, like 1Hz becuase it is controlling a small heater)

I have been following this excellent article, and have a question about one of the code used to interact with the ADC. To enable the ADC on PB0, this code is used:

ADMUX = 0<<MUX0;               // ADC0 (PB0)

For this first line, I understand that it is shifting 0 into the ADMUX register by MUX0 positions. There are only two bits in the ADMUX register, so shouldn’t it just be shifted twice to set both bits to 0? Why use MUX0 instead of (binary)10 or (decimal)2?

ADCSRA = 1<<ADEN | 3<<ADPS0;   // Enable ADC, 125kHz clock


This second line is a bit more complicated.
The first part seems to shift in a 1 to the ADEN bit which enables the ADC.
There is then a | (or) symbol
The next part seems to set the ADPS0 register to a division factor of 8 by shifting a 3 in. (Since clock is 1MHz, so 1MHz/8 = 125kHz). What is the point of the | between the two statements on this line?

I really appreciate any help, I understand these are very basic questions. :slight_smile:

I thought a fun way to learn would be by using one of the simplest MCU’s, the ATtiny 10.

I’m not sure that that’s a good idea. The principles are the same regardless of chip type, and there is more likely to be explanations and whatnot (and easier to look at thing) on a bigger chip like the ATmega328 used on the Arduino. Sometimes the really tiny chips are “simplified” to the point where they are more difficult to understand…

Anyway, you don’t seem to have run into that sort of problem yet.

ADMUX = 0<<MUX0;               // ADC0 (PB0)

I understand that it is shifting 0 into the ADMUX register by MUX0 positions. There are only two bits in the ADMUX register, so shouldn’t it just be shifted twice to set both bits to 0? Why use MUX0 instead of (binary)10 or (decimal)2?

I would NOT think of this as “shifting into the ADMUX.” It is setting the ADMUX register to a constant, where the constant is being CONSTRUCTED at compile time from a bunch of constant values, in a way that happens to use shifts.
The construct [b](BITFIELDVALUE<<LOWESTBITPOSITION)[/b] works to construct that part of the register contents regardless of the actual bit position, or the value of the field, or even the size of the field. Since the Atmel convention is to identify bits by bit-number (0<<MUX0) in this case, is a particularly boring example, since the value is 0, the position is zero, and it’s the only field in the register. 0, shifted by 0 bits, is still 0. They could have just said “ADMUX=0;”
But using the symbolic form, the statement would continue to work even if the bits moved around (because it’s a different chip, for example.)

ADCSRA = 1<<ADEN | 3<<ADPS0;   // Enable ADC, 125kHz clock

This second line is a bit more complicated. The first part seems to shift in a 1 to the ADEN bit which enables the ADC.
There is then a | (or) symbol
The next part seems to set the ADPS0 register to a division factor of 8 by shifting a 3 in. (Since clock is 1MHz, so 1MHz/8 = 125kHz). What is the point of the | between the two statements on this line?

Again, this just constructs a constant. In this case, the register contains multiple bits and bitfields that we want to set, and to save time we’d like to set them all to the desired values at the same time. We want the prescaler field (3 bits: ADPS0…2) set to a value of 3 (0b011), and we want the one-bit ADEN field set to one as well. (3<<ADPS0) gives us the first part, and (1<<ADEN) the second part, and then we want to logically OR them together ("|") to get 0b10000011, that is then written to the ADCSRA register…
See also [TUT] [C] Bit manipulation (AKA "Programming 101") | AVR Freaks