ATmega32 Switch Toggle Program
ATMega32 switch code is extremely simple to implement, and this article looks into how to write the code to make an LED light up when a switch is pressed. The atmega32-switch-code.c program tests the switch input to the ATMega32 Development System. There are many ways to write the code, but I have chosen the simplest way possible so everyone can understand. Therefore, instead of using "case", I am using "if", in C programming.
In this system, each switch has a LED behind it, and I have written this code so that when you press a switch, the LED behind it lights up. It functions as a mechanical push-button selector, or a locking switch, because you need to press the same switch again to turn OFF the LED.
Bit Operators in C
1: PORTC |= 0x01; // Set bit 0 only.
2: PORTC &= ~0x01; // Clear bit 0 only.
3: PORTC ^= 0x01; // Toggle bit 0 only.
4: PORTC & 0x01; // Test bit 0 only.
5: PORTC |= 0x80; // Set bit 7 only.
You can set, toggle, clear, and test, bits using bit operators as shown above. However, as you can see, the bit masks are in hexadecimal and not very human friendly to read.
Avr-libc has a special macro known as Bit Value, and a function "_BV ()", which takes the bit number and converts it to the appropriate bit mask. By using this macro, it is possible to specify the actual bit number without having to figure out the bit mask in hexadecimal. Therefore, it makes programming easier.
For Example
1: TCCR2 = _BV(COM20)|_BV(CTC2)|_BV(CS20);
2: DDRD = _BV(PD7);
Is the same as
1: TCCR2 = (1<<COM20)|(1<<CTC2)|(1<<CS20);
2: DDRD = (1<<PD7);
_BV The User Friendly Macro
1: PORTC |= _BV(2); // Set bit 2 only.
2: PORTC &= ~(_BV(1)); // Clear bit 1 only.
3: PORTC ^= _BV(5); // Toggle bit 5 only.
As you can see, this notation is simpler as you only have to specify the bit number instead of the mask.
Built-in Functions
The Avr-libc contains a built in macro function for reading switch input, which is the following.
1: bit_is_clear(SENSE_PORTC, SWITCHn_BIT);
When the switch is pressed, the I/O pin pulls to the ground. In the ATMega32, the PINx register reflects the state of the pins, and when the pin is at 0 V the bit becomes binary 0, and when the pin is at 5 V, the bit becomes binary 1. Therefore, you simply have to read the PINx register.
In this circuit, the switches connect to the PortC. We therefore detect switch input through the PinC register, and output through PortC register. The PinC register redefines as "SENSE_PORTC" which is more human friendly.
My Prototype Functions
Here are some of my functions and their descriptions. They are extremely simple to understand and use.
int switchn_is_pressed();
This function checks to see if a particular switch n was pressed, where n is a number between 0 and 7, representing the seven switches. When the switch is not pressed, the function returns a binary 0, and when it is depressed it returns a binary 1.
If pressed, it waits for 25-milliseconds and checks again. If the switch remains pressed after 25 milliseconds, then the function considers it de-bounced and lights the LED.
void toggle_ledn();
This function simply toggles the led n in question, where n is a number from 0 to 7. The LED lights when the switch is pressed, and turns OFF with the press of the same switch.
void init_io();
This function initialises the ports. The LEDs connect to PortA therefore; the DDRA (Data Direction Register) bits require setting to binary 0b11111111. The function also enables the internal pull-up resistors.
Debounce
When a switch is pressed, it usually "bounces" before settling. It is usually a damped harmonic response with duration is in the millisecond range. It happens so fast that it is usually not a problem in slow analogue circuits. However, in digital electronics, a fast microcontroller can detect the bounce as well, and therefore a single switch press would register as multiple presses.
Determining the bounce duration of a particular switch is very easy and usually done using an oscilloscope. Many new switches have an average rating for bounce specified in their documentation. In highly critical systems, bounce filtering involves a combination of interrupt circuits as well as software polling. However, in this application I am using only software techniques.
In this program, I am using the DEBOUNCE_TIME variable set to a 25-millisecond delay. This is the amount of time to wait before sensing the switch press again. Once the switch is considered pressed the LED output is toggled.
Engaging Internal Pull-up Resistors
You enable the internal pull-up resistors by sending binary 1 to the port. For example, the following statement enables the internal pull-up resistors.
SWITCH_PORT |= _BV (SWITCHn_BIT);
The SWITCH_PORT is PortC, which enables the individual bit n for the port.
ATMega32 Switch Code
1: /********************************************
2: Author:Peter J. Vis
3: First written:8 Dec 1999
4: Last updated: 12 Dec 2013
5:
6: MCU:ATmega32
7: Crystal:16 MHz
8: Platform:Development System
9:
10: URL:https://www.petervis.com
11:
12: LIMITATIONS:
13: This work may not be used by bloggers
14: without prior written permission.
15:
16: PURPOSE:
17: Push-button selector. When you press a switch,
18: the led associated with it lights up.
19:
20: CIRCUIT:
21: Each switch has an LED next to it.
22: Switch on PC0 has LED on PA0 next to it.
23: Switch on PC1 has LED on PA1 next to it.
24: Switch on PC2 has LED on PA2 next to it.
25: etc...
26: ********************************************/
27:
28: // 16 MHz crystal oscillator on my Development
29: // System.
30: #define F_CPU 16000000UL
31:
32:
33: // Switch port.
34: #define SWITCH_PORT PORTC
35:
36: // Input register - for reading pins.
37: #define SENSE_PORTC PINC
38:
39: // Switch bits individually defined.
40: #define SWITCH0_BIT PC0
41: #define SWITCH1_BIT PC1
42: #define SWITCH2_BIT PC2
43: #define SWITCH3_BIT PC3
44: #define SWITCH4_BIT PC4
45: #define SWITCH5_BIT PC5
46: #define SWITCH6_BIT PC6
47: #define SWITCH7_BIT PC7
48:
49: // LED port.
50: #define LED_PORT PORTA
51:
52: // LED bits are individually defined.
53: #define LED0_BIT PA0
54: #define LED1_BIT PA1
55: #define LED2_BIT PA2
56: #define LED3_BIT PA3
57: #define LED4_BIT PA4
58: #define LED5_BIT PA5
59: #define LED6_BIT PA6
60: #define LED7_BIT PA7
61:
62:
63: // LED data direction register.
64: #define LED_DDR DDRA
65:
66: // Time to wait while "de-bouncing"
67: // the switch.
68: #define DEBOUNCE_TIME 25
69:
70: // Time to wait after a switch is
71: // depressed.
72: #define LOCK_INPUT_TIME 250
73:
74:
75: #include <avr/io.h>
76: #include <inttypes.h>
77: #include <util/delay.h>
78:
79:
80: // Function prototypes.
81: void delay_ms(uint16_t ms);
82: void init_io();
83: int switch0_is_pressed();
84: int switch1_is_pressed();
85: int switch2_is_pressed();
86: int switch3_is_pressed();
87: int switch4_is_pressed();
88: int switch5_is_pressed();
89: int switch6_is_pressed();
90: int switch7_is_pressed();
91:
92: void toggle_led0();
93: void toggle_led1();
94: void toggle_led2();
95: void toggle_led3();
96: void toggle_led4();
97: void toggle_led5();
98: void toggle_led6();
99: void toggle_led7();
100:
101: int
102: main (void)
103: {
104: init_io();
105: while (1)
106: {
107: if (switch0_is_pressed())
108: {
109: toggle_led0();
110: _delay_ms(LOCK_INPUT_TIME);
111: }
112:
113: if (switch1_is_pressed())
114: {
115: toggle_led1();
116: _delay_ms(LOCK_INPUT_TIME);
117: }
118:
119:
120: if (switch2_is_pressed())
121: {
122: toggle_led2();
123: _delay_ms(LOCK_INPUT_TIME);
124: }
125:
126:
127: if (switch3_is_pressed())
128: {
129: toggle_led3();
130: _delay_ms(LOCK_INPUT_TIME);
131: }
132:
133:
134: if (switch4_is_pressed())
135: {
136: toggle_led4();
137: _delay_ms(LOCK_INPUT_TIME);
138: }
139:
140:
141: if (switch5_is_pressed())
142: {
143: toggle_led5();
144: _delay_ms(LOCK_INPUT_TIME);
145: }
146:
147:
148: if (switch6_is_pressed())
149: {
150: toggle_led6();
151: _delay_ms(LOCK_INPUT_TIME);
152: }
153:
154:
155: if (switch7_is_pressed())
156: {
157: toggle_led7();
158: _delay_ms(LOCK_INPUT_TIME);
159: }
162:
163: }
164: }
165:
166:
167:
168: // Function to initialize port
169: // settings.
170:
171: void
172: init_io()
173: {
174: // Set Data Direction Register
175: // to output mode for all LEDs.
176:
177: LED_DDR = 0b11111111;
178:
179:
180:
181: // Engage the internal pull-up resistors for
182: // for all switches. Make it so.
183: SWITCH_PORT |= _BV(SWITCH0_BIT);
184: SWITCH_PORT |= _BV(SWITCH1_BIT);
185: SWITCH_PORT |= _BV(SWITCH2_BIT);
186: SWITCH_PORT |= _BV(SWITCH3_BIT);
187: SWITCH_PORT |= _BV(SWITCH4_BIT);
188: SWITCH_PORT |= _BV(SWITCH5_BIT);
189: SWITCH_PORT |= _BV(SWITCH6_BIT);
190: SWITCH_PORT |= _BV(SWITCH7_BIT);
191:
192: }
193:
194:
195:
196: // Functions to sense each switch individually.
197:
198: int switch0_is_pressed()
199:
200: {
201: // The switch is pressed
202: // when the switch0_BIT is clear.
203: if (bit_is_clear(SENSE_PORTC, SWITCH0_BIT))
204: {
205: _delay_ms(DEBOUNCE_TIME);
206: if (bit_is_clear(SENSE_PORTC, SWITCH0_BIT))
207: return 1;}
208: return 0;
209: }
210:
211: int switch1_is_pressed()
212:
213: {
214: // The switch is pressed
215: // when switch1_BIT is clear.
216: if (bit_is_clear(SENSE_PORTC, SWITCH1_BIT))
217: {
218: _delay_ms(DEBOUNCE_TIME);
219: if (bit_is_clear(SENSE_PORTC, SWITCH1_BIT))
220: return 1;}
221: return 0;
222: }
223:
224: int switch2_is_pressed()
225:
226: {
227: // The switch is pressed
228: // when switch2_BIT is clear.
229: if (bit_is_clear(SENSE_PORTC, SWITCH2_BIT))
230: {
231: _delay_ms(DEBOUNCE_TIME);
232: if (bit_is_clear(SENSE_PORTC, SWITCH2_BIT))
233: return 1;}
234: return 0;
235: }
236:
237: int switch3_is_pressed()
238:
239: {
240: // The switch is pressed
241: // when switch3_BIT is clear.
242: if (bit_is_clear(SENSE_PORTC, SWITCH3_BIT))
243: {
244: _delay_ms(DEBOUNCE_TIME);
245: if (bit_is_clear(SENSE_PORTC, SWITCH3_BIT))
246: return 1;}
247: return 0;
248: }
249:
250: int switch4_is_pressed()
251:
252: {
253: // The switch is pressed
254: // when switch4_BIT is clear.
255: if (bit_is_clear(SENSE_PORTC, SWITCH4_BIT))
256: {
257: _delay_ms(DEBOUNCE_TIME);
258: if (bit_is_clear(SENSE_PORTC, SWITCH4_BIT))
259: return 1;}
260: return 0;
261: }
262:
263: int switch5_is_pressed()
264:
265: {
266: // The switch is pressed
267: // when switch5_BIT is clear.
268: if (bit_is_clear(SENSE_PORTC, SWITCH5_BIT))
269: {
270: _delay_ms(DEBOUNCE_TIME);
271: if (bit_is_clear(SENSE_PORTC, SWITCH5_BIT))
272: return 1;}
273: return 0;
274: }
275:
276: int switch6_is_pressed()
277:
278: {
279: // The switch is pressed
280: // when switch6_BIT is clear.
281: if (bit_is_clear(SENSE_PORTC, SWITCH6_BIT))
282: {
283: _delay_ms(DEBOUNCE_TIME);
284: if (bit_is_clear(SENSE_PORTC, SWITCH6_BIT))
285: return 1;}
286: return 0;
287: }
288:
289:
290: int switch7_is_pressed()
291:
292: {
293: // The switch is pressed
294: // when switch7_BIT is clear.
295: if (bit_is_clear(SENSE_PORTC, SWITCH7_BIT))
296: {
297: _delay_ms(DEBOUNCE_TIME);
298: if (bit_is_clear(SENSE_PORTC, SWITCH7_BIT))
299: return 1;}
300: return 0;
301: }
302:
303:
304:
305: // Functions to toggle LEDs individually.
306: // This is the long way - But easier to see.
307: // -----------------------------------------
308:
309: void toggle_led0()
310:
311: {
312: LED_PORT ^= _BV(LED0_BIT);
313: }
314:
315:
316: void toggle_led1()
317:
318: {
319: LED_PORT ^= _BV(LED1_BIT);
320: }
321:
322:
323: void toggle_led2()
324:
325: {
326: LED_PORT ^= _BV(LED2_BIT);
327: }
328:
329:
330: void toggle_led3()
331:
332: {
333: LED_PORT ^= _BV(LED3_BIT);
334: }
335:
336:
337: void toggle_led4()
338:
339: {
340: LED_PORT ^= _BV(LED4_BIT);
341: }
342:
343:
344: void toggle_led5()
345:
346: {
347: LED_PORT ^= _BV(LED5_BIT);
348: }
349:
350:
351: void toggle_led6()
352:
353: {
354: LED_PORT ^= _BV(LED6_BIT);
355: }
356:
357:
358: void toggle_led7()
359:
360: {
361: LED_PORT ^= _BV(LED7_BIT);
362: }
The while(1) loop defines an infinite loop, and all the time is spent in this loop sensing the PinC register for a bit to go low and therefore cleared.