ATMEGA16 Programming

Thread Starter

Mopinion

Joined Apr 24, 2011
6
Hello all,
I am trying to set up a program that takes in an input from an ADC (potentiometer) and output it to an LED via PWM. I am doing this using the ATmega16 microcontroller. Also, I have set it up so that if switch 0 is pressed the output from the PWM is taken directly from the ADC. If switch 1 is pressed the output from the PWM is the full voltage if the ADC is greater than 128. I have gotten the ADC and PWM to work together with the LED, but when introducing the if statements for switch 0 and 1 it stops working.
If anyone could help me that would be great.
The code is below:
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 3686400
#include <util/delay.h>

// Function Prototypes
void PWM_init(void);
void ADC_init(void);
void start_convert(void);

// Global variables
volatile unsigned char dim;

int main(void)
{
// initialize IO port, interrupt and PWM
// Set Port B as output port for the LEDs
DDRB = 0xFF;
PORTB = 0xFF;
// Set Port A as input ADC
DDRA = 0x00;
PORTA = 0xFF;
// Set Port C as switch input
DDRC = 0x00;
PORTC = 0xFF;

// enable global variable
sei();
// Initialize PWM
PWM_init();
// Initialize PWM
ADC_init();

while (1)
{
// keep looping the conversion process

start_convert();
// so the human eye can track bit changes
_delay_ms(40);


}
return 0;
}

// PWM Interrupt Service Routine
ISR(TIMER1_OVF_vect)
{
// If switch 0 is pressed then output through PWNM
if(PINC=0xFE)
{
// Update the register by the binary value presented
// at Port A
OCR1A = dim;
}

if(PINC=0xFE)
{
if(~ADCH > 128)
{
OCR1A = 0xFF;
}
}
}

// ADC Interrupt Service Routine
ISR(ADC_vect)
{
// Read from ADC data register (MSB)
dim = ~ADCH;
}

// Inialization of PWM
void PWM_init(void)
{
// Initialize OCIA as output port
DDRD |= (1 << PD5);
// Initialize Timer/Counter 1 overflow interrupt
TIMSK |= (1 << TOIE1);
// Program COM1A so that OCIA is set at TOP
TCCR1A |= (1 << COM1A1);
// Program WGM to Phase-correct, 8-bit mode
TCCR1A |= (1 << WGM10);
// Set Timer/Counter 1 prescalar to Clk/1
TCCR1B |= (1 << CS10);
}

// Inialization of ADC
void ADC_init(void)
{
// Enable ADC and ADC interrupt
ADCSRA |= 0x88; // or ADCSRA |= (1<<ADEN)|(1<<ADIE)
// Select ADC0 as single-ended analog input
ADMUX |= 0xE0; // Set lower 5 bits to 0
// Select the 2.56V internal voltage reference with external capacitor
// at the AREF pin
ADMUX |= (3<<REFS0);
// Left adjusted register (ADLAR = 1)
ADMUX |= (1<<ADLAR);
}

// Start conversion
void start_convert(void)
{
// Start Conversion
ADCSRA |= (1 << ADSC) | (1 << ADIE);
}
 

Kermit2

Joined Feb 5, 2010
4,162
It may not be the problem but in the code where the PWM INTERUPT SERVICE ROUTINE starts you have one more left bracket than you do right brackets. You may need to close it.
 

hgmjr

Joined Jan 28, 2005
9,027
I have added code formating tags (see the # sign ) in the menu icons above the editing window. It makes the code easier to follow... I also did a bit of indenting to help with readability.

Hello all,
The code is below:
Rich (BB code):
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 3686400
#include <util/delay.h>
 
// Function Prototypes
void PWM_init(void);
void ADC_init(void);
void start_convert(void);
 
// Global variables
volatile unsigned char dim;
 
int main(void)
{
   // initialize IO port, interrupt and PWM
   // Set Port B as output port for the LEDs
   DDRB = 0xFF;
   PORTB = 0xFF;
   // Set Port A as input ADC   
   DDRA = 0x00;
   PORTA = 0xFF;
   // Set Port C as switch input
   DDRC = 0x00;
   PORTC = 0xFF;
 
   // enable global variable
   sei();
   // Initialize PWM
   PWM_init();
   // Initialize PWM 
   ADC_init();
 
   while (1)
   {
      // keep looping the conversion process
 
      start_convert();
      // so the human eye can track bit changes
      _delay_ms(40); 
   }
   return 0;
}
 
// PWM Interrupt Service Routine
ISR(TIMER1_OVF_vect)
{
   // If switch 0 is pressed then output through PWNM
   if(PINC=0xFE)
   {
      // Update the register by the binary value presented
      // at Port A
      OCR1A = dim;
   }
 
   if(PINC=0xFE)
   {
      if(~ADCH > 128)
      {
         OCR1A = 0xFF;
      }
   }
}
 
// ADC Interrupt Service Routine
ISR(ADC_vect)
{
   // Read from ADC data register (MSB)
   dim = ~ADCH;
}
 
// Inialization of PWM
void PWM_init(void)
{
   // Initialize OCIA as output port
   DDRD |= (1 << PD5);
   // Initialize Timer/Counter 1 overflow interrupt
   TIMSK |= (1 << TOIE1); 
   // Program COM1A so that OCIA is set at TOP
   TCCR1A |= (1 << COM1A1);
   // Program WGM to Phase-correct, 8-bit mode
   TCCR1A |= (1 << WGM10); 
   // Set Timer/Counter 1 prescalar to Clk/1
   TCCR1B |= (1 << CS10); 
}
 
// Inialization of ADC
void ADC_init(void)
{
   // Enable ADC and ADC interrupt
   ADCSRA |= 0x88; // or ADCSRA |= (1<<ADEN)|(1<<ADIE)
   // Select ADC0 as single-ended analog input
   ADMUX |= 0xE0; // Set lower 5 bits to 0
   // Select the 2.56V internal voltage reference with external capacitor
   // at the AREF pin
   ADMUX |= (3<<REFS0);
   // Left adjusted register (ADLAR = 1)
   ADMUX |= (1<<ADLAR);
}
 
// Start conversion
void start_convert(void)
{
   // Start Conversion
   ADCSRA |= (1 << ADSC) | (1 << ADIE);
}
 

Thread Starter

Mopinion

Joined Apr 24, 2011
6
It may not be the problem but in the code where the PWM INTERUPT SERVICE ROUTINE starts you have one more left bracket than you do right brackets. You may need to close it.
hmm... by my count there are 4 open and 4 close brackets... Also, i'm pretty sure i would have gotten an error if i have uneven number of brackets when compiling. Can't figure out what's wrong. When testing it seems it automatically only runs the PWM case and not the other case.
 

hgmjr

Joined Jan 28, 2005
9,027
A couple of comments might be useful.

1. You test the switch with the assignment operator '='. I think you need to use the "==" comparison operator.

2. I think you would be better off if you used a mask with your switch state test.

3. I think you would be better served if you placed your switch test in the main loop rather than put it into the interrupt service routine.

hgmjr
 

Thread Starter

Mopinion

Joined Apr 24, 2011
6
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 3686400
#include <util/delay.h>

// Function Prototypes
void PWM_init(void);
void ADC_init(void);
void start_convert(void);

// Global variables
volatile unsigned char dim;
volatile unsigned char x;

int main(void)
{
// initialize IO port, interrupt and PWM
// Set Port B as output port for the LEDs
DDRB = 0xFF;
PORTB = 0xFF;
// Set Port A as input ADC
DDRA = 0x00;
PORTA = 0xFF;
// Set Port C as switch input
DDRC = 0x00;
PORTC = 0xFF;

// enable global variable
sei();
// Initialize PWM
PWM_init();
// Initialize PWM
ADC_init();

while (1)
{
// keep looping the conversion process

start_convert();
// so the human eye can track bit changes
_delay_ms(40);
// If switch 0 is pressed then output through PWNM
if((PINC&0x01)==0x00)
{
// Update the register by the binary value presented
// at Port A
x = dim;
}

if((PINC&0x02)==0x00)
{
if(~ADCH > 128)
{
x = 0xFF;
}
}
}
return 0;
}

// PWM Interrupt Service Routine
ISR(TIMER1_OVF_vect)
{
OCR1A = x;
}

// ADC Interrupt Service Routine
ISR(ADC_vect)
{
// Read from ADC data register (MSB)
dim = ~ADCH;
}

// Inialization of PWM
void PWM_init(void)
{
// Initialize OCIA as output port
DDRD |= (1 << PD5);
// Initialize Timer/Counter 1 overflow interrupt
TIMSK |= (1 << TOIE1);
// Program COM1A so that OCIA is set at TOP
TCCR1A |= (1 << COM1A1);
// Program WGM to Phase-correct, 8-bit mode
TCCR1A |= (1 << WGM10);
// Set Timer/Counter 1 prescalar to Clk/1
TCCR1B |= (1 << CS10);
}

// Inialization of ADC
void ADC_init(void)
{
// Enable ADC and ADC interrupt
ADCSRA |= 0x88; // or ADCSRA |= (1<<ADEN)|(1<<ADIE)
// Select ADC0 as single-ended analog input
ADMUX |= 0xE0; // Set lower 5 bits to 0
// Select the 2.56V internal voltage reference with external capacitor
// at the AREF pin
ADMUX |= (3<<REFS0);
// Left adjusted register (ADLAR = 1)
ADMUX |= (1<<ADLAR);
}

// Start conversion
void start_convert(void)
{
// Start Conversion
ADCSRA |= (1 << ADSC) | (1 << ADIE);
}

-------------------------------------------------------------------------------
So i've slightly changed the code around. What happens now is that when I hold down switch 0 the PWM case kicks in, but when i hold down switch 1, the LED just switched off. So two problems:
1) If statement for the second switch (switch 1) does not work.
2) I have to hold down switch 0 or 1 in order for the case to work.
 

hgmjr

Joined Jan 28, 2005
9,027
Used code tags to aid in reading the code...

Rich (BB code):
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 3686400
#include <util/delay.h>
 
// Function Prototypes
void PWM_init(void);
void ADC_init(void);
void start_convert(void);
 
// Global variables
volatile unsigned char dim;
volatile unsigned char x;
 
int main(void)
{
   // initialize IO port, interrupt and PWM
   // Set Port B as output port for the LEDs
   DDRB = 0xFF;
   PORTB = 0xFF;
   // Set Port A as input ADC 
   DDRA = 0x00;
   PORTA = 0xFF;
   // Set Port C as switch input
   DDRC = 0x00;
   PORTC = 0xFF;
 
   // enable global variable
   sei();
   // Initialize PWM
   PWM_init();
   // Initialize PWM 
   ADC_init();
 
   while (1)
   {
      // keep looping the conversion process
 
      start_convert();
      // so the human eye can track bit changes
      _delay_ms(40);
      // If switch 0 is pressed then output through PWNM
      if((PINC&0x01)==0x00)
      {
         // Update the register by the binary value presented
         // at Port A
         x = dim;
      }
 
      if((PINC&0x02)==0x00)
      {
         if(~ADCH > 128)
         {
            x = 0xFF;
         }
      } 
   }
   return 0;
}
 
// PWM Interrupt Service Routine
ISR(TIMER1_OVF_vect)
{
   OCR1A = x;
}
 
// ADC Interrupt Service Routine
ISR(ADC_vect)
{
   // Read from ADC data register (MSB)
   dim = ~ADCH;
}
 
// Inialization of PWM
void PWM_init(void)
{
   // Initialize OCIA as output port
   DDRD |= (1 << PD5);
   // Initialize Timer/Counter 1 overflow interrupt
   TIMSK |= (1 << TOIE1); 
   // Program COM1A so that OCIA is set at TOP
   TCCR1A |= (1 << COM1A1);
   // Program WGM to Phase-correct, 8-bit mode
   TCCR1A |= (1 << WGM10); 
   // Set Timer/Counter 1 prescalar to Clk/1
   TCCR1B |= (1 << CS10); 
}
 
// Inialization of ADC
void ADC_init(void)
{
   // Enable ADC and ADC interrupt
   ADCSRA |= 0x88; // or ADCSRA |= (1<<ADEN)|(1<<ADIE)
   // Select ADC0 as single-ended analog input
   ADMUX |= 0xE0; // Set lower 5 bits to 0
   // Select the 2.56V internal voltage reference with external capacitor
   // at the AREF pin
   ADMUX |= (3<<REFS0);
   // Left adjusted register (ADLAR = 1)
   ADMUX |= (1<<ADLAR);
}
 
// Start conversion
void start_convert(void)
{
   // Start Conversion
   ADCSRA |= (1 << ADSC) | (1 << ADIE);
}
-------------------------------------------------------------------------------
So i've slightly changed the code around. What happens now is that when I hold down switch 0 the PWM case kicks in, but when i hold down switch 1, the LED just switched off. So two problems:
1) If statement for the second switch (switch 1) does not work.
2) I have to hold down switch 0 or 1 in order for the case to work.
 

hgmjr

Joined Jan 28, 2005
9,027
So are you wanting to be able to press the switch and then release it and have the circuit work as though you were continually holding down the switch?

If so, then you need to introduce a variable (flag) that will toggle states when you press the appropriate switch. You can then test this flag to tell which state you are in.

hgmjr
 

Thread Starter

Mopinion

Joined Apr 24, 2011
6
Yes, I apologize about the tags. Just noticed what you were talking about. I would like to do that, but i would first like to make sure that the switch 1 case works before editing it so that i don't need to hold down the switches... I have been trying for awhile now.
 

hgmjr

Joined Jan 28, 2005
9,027
Have you checked the wiring of switch 1 to make sure that it is connected to the pin you are testing and that it is operating correctly?

hgmjr
 

Thread Starter

Mopinion

Joined Apr 24, 2011
6
The microcontroller is on a development board. The only wiring involved is moving the jumper cables around, i have double checked them to make sure that they were correct. I am currently baffled.
 

Thread Starter

Mopinion

Joined Apr 24, 2011
6
The microcontroller is on a development board, along with the switches and the LED's. The jumper cables are the only connections I have control over, and I have checked them over and over again to make sure they are in the right places. I am baffled as to what is wrong with the program.
 

hgmjr

Joined Jan 28, 2005
9,027
Here is a quick check you can try.

Temporarily assign the action for switch 1 into the 'if' statement for switch 0. If switch 0 performs the action properly then you have an issue with switch 1 and/or its wiring. If it does not perform the action then there is a code issue.

hgmjr
 
Top