ATmega32 Switch Toggle Program

ATMega32 Switch Input

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.