New user with questions about input

Thanks to all who help on this forum!

I’m a retired guy, old enough to have started on vacuum tubes in the 60’s. My son gave me Atmel 168 microcontrollers & parts for Christmas and I’m having a great time learning to use it. I really enjoyed the first two tutorials at Sparkfun.com (many thanks to their writer) and I got it up and running without difficulty.

I’m interested in using switches to input to the microcontroller. Couldn’t find much information on how to do that, so I’ve puzzled it out by trial and error. I’m using 10k resistors to ground to pull down each of the PB0 to PB2 pins and I touch the pin side of a resistor with a wire connected to the 5 V supply to switch it on (no pushbutton switches handy). My program simply reads PORTB and writes to PORTC repeatedly:

int x;

int main (void)

{ ioinit(); //Setup IO pins and defaults

DDRB = 0b00000000; // set port B for input

while(1)

{ x = PINB; // read portB

PORTC = x; // output to portC

delay_ms(500);

}

return(0);

I have the Port C pins connected through resistors and LED’s to ground to show the output.

It works! When I pull PB0 high, the LED on PC0 lights! And the other two inputs also work. I read about switch debouncing, but had no trouble at all with it so far.

I’d like to write this up and make it available to other newbies interested in connecting their microcontrollers to switch inputs, but first I have lots of questions for you experienced people.

Why do I have to put “x = PINB” instead of “x = PORTB”? Where was I supposed to find this information - I must have missed lots of other stuff with it. I got the PINB idea from a program that had this line for reading:

pin_status = ~PINB & (_BV(PINB0) | _BV(PINB1));

Why would that be used when PINB alone works?

Can I use PB3 through PB5 as well, even though they are connected to my serial programmer? Is it safe to program with the pull down resistors in place on those pins?

In some programs I see “volatile int x” instead of “int x”. What does the word “volatile” do? Sorry, I took a C course years ago but C doesn’t stick in the mind well. Pascal is much better for amateurs and old people!

It would be much nicer to use interrupts to respond to switches - is there somewhere to find help on that?

The censor declined to post this the first time I wrote; hope it works this time.

Thanks!

Harvey,

Here are some answers for you:

1- Why do you read from PIN and write to PORT?

PIN is the input register for the external inputs. If you look at the logic diagram for the AVR, you will see that they wire the inputs to a different register than the output. This is actually beneficial because it means when changing the output of a single pin, you can say:

X = PORTC;

X = X | 64;

PORTC = X;

This would set bit 6 high without changing the other 7 bits. It works because reading PORTC reads the existing output values of the port (so you read the output values of all 8 bits, then change bit 6 by or-ing with 64 and then store the result). Otherwise, you would always need to store the output bits somewhere else in a register or memory. Its really just a convenience, but a very nice one.

2- Can you use PB3-5 if they are used by the programmer?

Yes, you can use the pins connected to the serial programmer, but only if you can wire them without the pullups/pulldowns I believe (this may depend on your programmer). Essentially, your secondary circuitry needs to avoid altering the pins while programming is taking place. See below for a method of interfacing switches which avoids the issue.

3- What does volatile mean in C?

It tells the C compiler that the variable can be changed independent of the program. Its often used when accessing I/O registers because the registers can change based on hardware changes. Otherwise, the compiler could potentially load the value and keep it around internally and not realize it changes over time. Its essentially disabling an optimization that the compiler might do (and which is beneficial in any other situation). Normally PINB and POUTB would be declared volatile, but not a local variable like X (X will not change “behind your back” whereas PINB will change based on the inputs to the microcontroller in real time).

4- How to use interrupts?

It can certainly be done and I have done it many times, but I don’t have an example handy. Its trickier than you might think because of issues like debouncing and that interrupts are much harder to setup. If you want to learn about interrupts, I would suggest experimenting with the timer. A very simple challenge is to use a timer and interrupt to cause an LED to blink.

5- Why don’t you need to debounce?

Actually, you probably do. Debouncing is used to make the switch signal change states cleanly. Because you are essentially connecting the switch to an LED, its impossible for your eye to register the noise that occurs when you first connect the switch. If you had a oscilloscope connected, I suspect you would see it. There are two common methods to debounce a switch-- hardware and software. The hardware method involves using a small capacitor in parallel with the switch. The charge/discharge action of the capacitor reduces the switch noise. In the software method, after detecting a switch change, you set a timer and essentially ignore any further switch changes until the timer expires (and timer is typically only a few milliseconds). I favor the latter method myself.

6- Is there an easier way to interface a switch to an AVR?

Yes there is. The AVR has internal pullup resistors on all the ports which can be optionally enabled. Enable/disable the pullup resistor by setting the corresponding bit in the PORT register. When the direction (DDR) register is set to output, the bit in the PORT register is the output value. When the direction register is set to input, the bit in the PORT register controls the internal pullup. Thus, you just need to connect the switch between the port and ground and set the internal pullup. The other nice part is that this technique will definitely work on PB3-5 with your programmer (as long as you dont press the switches while programming).

To make this work, change your setup code to:

DDRB = 0b00000000; // set port B for input

PORTB = 0b00000111; // enable pullups on PB0-PB2

Note that the switch input will be inverted. A “1” means the switch is not pushed and a “0” means it is. So with your code, the LED will go out when you push the button (or ground the pin since you don’t have switches handy).

If you want the LED to follow the pin, change the code to:

while(1)

{ x = PINB; // read portB

x = x ^ 7; // invert PB0-PB2

PORTC = x; // output to portC

delay_ms(500);

}

return(0);

Enjoy!

Thanks, Vraz! Really appreciate your clear and detailed answers . . . I’m actually busy today but will make good use of the information very soon.

Thanks again for the internal pullup idea - it works perfectly for the first 3 pins of port B. It does not work for the other pins, which are also connected to a serial programmer. I’m sure it will work when the programmer is disconnected.

Output from portC is limited to 6 bits, PC0 through 5; PC6 is used as the reset from the serial programmer and there doesn’t seem to be a PC7 on the pinout diagram.

I used this code to read port B and write to port C:

DDRB = 0b00000000; // set port B for input (da direction register)

PORTB = 255; // set port B pullups

//When the direction register is set to input, the bit in the PORT

//register controls the internal pullup.

while(1) // copy input B to output C forever

{ PORTC = PINB;

delay_ms(500);

}

The port B pins are connected to ground through switches.

I have LEDs on the first 6 port C pins (through 270 ohm resistors to ground) to display the output.

Harvey,

You are my fellow.

I also started with microcontrolers in my late age.

I made a picture where you can see how a port-pin in AVR looks.

Maybe it helps you to understand the port registers.

http://www.mp222.wz.cz/Portb1.jpg

Wouldn’t you mind to write me for further discussion?

My email: kutejj(mark)email(dot)cz

Thanks, Visovian. That diagram does clarify the port.

Looks like you know a lot more about electronics than me!

I would like to try the ADC input next.

If I can learn enough, I would like to try to make an “inactivity monitor” for elderly or diabetic people. It would call for help if no one pressed a button for the set number of hours. I have no idea how to make a phone call with a microcontroller.

It does not work for the other pins, which are also connected to a serial programmer. I’m sure it will work when the programmer is disconnected.

This is surprising, but it probably depends on your programmer. I would expect most would tri-state the pins when the microcontroller is running (pretty sure my AVRISP does this). Curious-- did you try it with the programmer unplugged and what kind of programmer are you using?

The function of PC6 vs RESET is determined by one the fuse bits. However, since RESET is required for serial programming, you DON’T want to change its usage. It doesn’t hurt anything to write data into PC6/PC7 in this application so your little program is good.

Since you wired the LEDs from PORTC to LED+resistor to GND, they should be lit unless you press a button. You can reverse the effect by wiring +5V to the LED+resistor to PORTC (reversing the LED so the anode/cathod are connected correctly). Then the LEDs will be off unless you press a button (at which point the PORTC bit will complete the circuit from +5V through the LED+resistor to ground).

I tried it with the serial cable unplugged from the computer, but did not disconnect the serial programmer from the microcontroller. I’m using a “AVR STK Serial port dongle” from Sparkfun.

It is too bad you can’t get full 8 bit output because of the dual use of the 8th pin. It seems to me a better dual-use arrangement could have been made. For instance, why not combine the programming and serial communication features? No doubt the same device could be used for programming and for communication with the computer after programming if the chip was designed to begin programming on receipt of a certain code.

Extremely odd that it didn’t work with the programmer unplugged from the computer for at least PB4. Looking at the schematic for your programmer, PB4 is a straight connection (no other components) and PB3/PB5 only have zener diodes for voltage clamping.

In your program, did you include a DDRC=0b00111111 or similar? You didn’t include your PORTC initialization code in your listing. If you didn’t set PC3-PC5 to outputs, then it would appear like the inputs didn’t work. Looking back at your original code, you call an init() function, but the corresponding code was not included.

For instance, why not combine the programming and serial communication features?

There is always a tradeoff in small pin count devices as functions get overlapped. If you move up to the 40 pin devices, you will find more full ports (though many pins still have double functions).