I'm trying to use some previously developed sw from Github and ran across an interesting bit of sw coding.
He's using an Atmel ATtiny45 with digital I/O's on most of the PortB pins but PB3 is used as an analog (AtoD) input from an external potentiometer. In his code, he's got the following snippet:
if(PINB & MANCLK){
PORTB |= CLKOUT;
} else {
PORTB &= ~CLKOUT;
}
}
(Note: 'CLKOUT' is a digital output pin.)
"MANCLK" is the analog input pin at PB3 so, what does the line: "if(PINB & MANCLK)..." do? Does it actually consider all analog values coming in on that pin so, anything less than 2.5V is considered a logic low and anything over 2.5V is a high or, is this even a valid statement? Is 'MANCLK' simply to be considered as a "0" or a "1"? Not sure where to find good information on this particular situation.
Thanks for any help provided.
Regards, Grant
[update] Here's the code....
`
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#if !defined(TIMSK)
#define TIMSK TIMSK0
#endif
#if !defined(TIMER0_COMPA_vect)
#define TIMER0_COMPA_vect TIM0_COMPA_vect
#endif
// 100 us per tick
#define COUNT 199
#define TPSC (1<<CS01)
#define AUTO (1<<PB0) //Pin5, MOSI and EN (via RN33 Pins 4&5).
High = Enable
#define RUN (1<<PB1) //Pin6, MISO and ~HLT (via RN33 Pins 3&6).
Low = Halt
#define MANCLK (1<<PB3) //Pin2, Analog input, Manual Clock Speed
Setting (via potentiometer RV1)
#define CLKOUT (1<<PB4) //Pin3, CLK output
#define AUTOINT (1<<PCINT0)
#define RUNINT (1<<PCINT1)
#define CLKINT (1<<PCINT2)
#define MANDLY 500
volatile uint16_t count;
volatile uint8_t trigger;
volatile uint8_t manual;
uint16_t maxcnt;
void stopTimer(void) {
PORTB &= ~CLKOUT; // Clock output low
TCCR0B &= ~TPSC;
TCNT0;
count = 0;
trigger = 0;
}
void startTimer(void) {
PCMSK = 0;
TCCR0B |= TPSC;
}
void halt(void) {
cli();
stopTimer();
PCMSK = RUNINT|AUTOINT;
sei();
sleep_enable();
while(!(PINB & RUN)) sleep_cpu();
sleep_disable();
cli();
startTimer();
sei();
}
void manclk(void) {
cli();
stopTimer();
PCMSK = CLKINT|RUNINT|AUTOINT;
sei();
while(!(PINB & AUTO)) {
if(PINB & RUN) {
if(PINB & MANCLK) {
PORTB |= CLKOUT;
} else {
PORTB &= ~CLKOUT;
}
}
sleep_enable();
sleep_cpu();
sleep_disable();
}
cli();
startTimer();
sei();
}
int main(void)
{
DDRB = CLKOUT;
PORTB &= ~CLKOUT;
cli();
TCCR0A = 0;
TCCR0B = 0;
TCNT0 = 0;
TCCR0A |= (1 << WGM01);
OCR0A = COUNT; // Interrupts every 100us when Timer running
TCCR0B |= (1 << CS01); // Timer Start /8 pre-scaler
TIMSK |= (1 << OCIE0A);
GIMSK |= (1 << PCIE);
sei();
// Setup the ADC
//ADMUX |= (1 << MUX1); // Only using ADC2 (original)
ADMUX |= (1 << MUX0) | (1 << MUX1); //new code - MUX Select ADC3 @
PB3
ADCSRA |= (1 << ADEN);
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); // 125 kHz ADC clock
//Enable Sleeping
set_sleep_mode(SLEEP_MODE_IDLE);
while (1) {
//if RUN = HLT = Low ==> HALT
if (!(PINB & RUN)) {
halt();
continue;
}
//if AUTO = ENABLE = Low ==> manclk...if AUTO (ENABLE) == LOW ==>
MANCLK
if (!(PINB & AUTO)) {
manclk();
continue;
}
ADCSRA |= (1 << ADSC);
while (ADCSRA & (1 << ADSC));
maxcnt = 5000 / (64 - ADC/16);
if (trigger) {#
PORTB ^= CLKOUT;
trigger = 0;
}
sleep_enable();
sleep_cpu();
sleep_disable();
}
return 0; // never reached
}
ISR(TIMER0_COMPA_vect) {
count++;
if (count >= maxcnt) {
count = 0;
trigger = 1;
}
}
EMPTY_INTERRUPT(PCINT0_vect);`
If we expand some of the preprocessor macros, your code becomes easier to explain:
if (PINB & (1 << 3))
{
PORTB |= (1 << 4);
}
else
{
PORTB &= ~(1 << 4);
}
Now just looking at the code above we can tell that it does the following: it reads bit 3 of the PINB register and if it is 1, it sets bit 4 of the PORTB register to 1. Otherwise, it clears (sets to 0) bit 4 of the PORTB register.
If you don't understand why this is true, I recommend that you look up the definitions of the following C bitwise logic operators in a C book or C tutorial: &
|
, <<
, |=
, &=
.
A simpler way of describing the code is that it copies the value from bit 3 of PINB to bit 4 of PORTB.
Now to get a better understanding of what the code does, you need to find the ATtiny45 datasheet and read the definitions of the PINB and PORTB registers. Then you will discover the following:
Reading bit 3 of PINB is how you do a digital reading of PB3. It does not use the ADC and it does not have a super accurate threshold like you were thinking. The thresholds are defined somewhere in the "Electrical Specifications" section of the datasheet or similar.
Since bit 4 of DDRB is 1 in your program, PB4 is configured as a digital output. Therefore, writing a value to bit 4 of PORTB is how we set the output value of PB4. Writing a 0 to it makes it drive low (0 V), and writing a 1 to it makes it drive high.
In summary, this is some pretty basic AVR code that does a digital reading and writes that value to a digital output.