Controlling a Led via PWM + 1Hz square wave on the output project (ATMEGA328p)

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone.

 

I am studying micro controllers for the second time (I don't know much about these besides basic ATMega128 programming) and I'm looking for help in a school project. I need to create a project in which:

 

  • I control a led (or a fan) via PWM (through the timer counters, etc. etc.)
  • I create a 1Hz wave on the output of the microcontroller

 

I'm having trouble finding where to start. Besides having to build everything in a breadboard (ATMega328p, the led/fan, etc.), I have no idea how to start the code even though I have to write it in C (since I'm decent at it).

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi. I know I have and I tried to delete but I couldn't. I got the exercise wrong since I thought I had to make a led blink and not control it by pwm (like I should do). I removed all the components from the BreadBoard and I decided to start from scratch.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
I'm having trouble finding where to start.

 

1) Newbie's Guide to AVR Timers by abcminiuser (Link).

 

2) Pulse Width Modulation (PWM) by abcminiuser (Link).

 

3) PWM for complete idiots by cmwslw (Link).

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I came up with this code by reading the ATmega datasheet (I decided to use a ATMega88 instead of the 328 since it was the one I had at home. Not sure if changing all the ports again would take massive work or not).

 


#include <avr/interrupt.h>    
#include <stdio.h>
#include <avr/io.h>


volatile unsigned char count=2;   //variavel contador
volatile unsigned char led ;      //variavel para verificar se led está ligado ou desligado

void inic(void)
{
	PORTD=0b00000011;  //PD0 e PD3 como outputs, PD0 para o led a piscar, e PD3 para led pwm pois é o pin que contém o OC2B
	DDRD=0b00000011;   //1 igual a outputs.

	

	OCR0A = 243;	         // Timer counter 0 (8 bits, atmega88)
	TCCR0A =0b00000010;      // Funcionamento CTC, T=Prescaler/CLK *(OCR0 + 1)
	TCCR0B =0b00000101;      // Tempo=250ms, Prescaler=1024, CLK=1MHZ
	TIMSK0 |=0b00000010;     // ativa timer0 compare match A

	OCR2B=0;
	TCCR2A=0b00100011; // OC2A disconectado, modo fast pwm,(non-inverting mode) apaga OC2B em cp match, OC2B at bottom
	TCCR2B=0b00000011; // prescaler=32
	


	ADMUX=0b01100101;  // PC5(ADC5) para input, potenciómetro ligado a PC5, ajustamento á esquerda(ADLAR=1), AVCC com capacitor externo no pin AREF
	ADCSRA=0b10001011; //fator de divisão=8, permite a ativação do ADC



	sei(); // enable global das interrupcões

}

ISR(ADC_vect)  // Interrupção do ADC
{
    OCR2B=ADCH;             //tranfere valor do potenciometro para o led no PD3
	ADCSRA|=0b01000000;

}



ISR(TIMER0_COMPA_vect)   // Interrupção do led a piscar
{
	count--;                    //conta 250ms
	led=(PORTD & 0b00000001);   //Led que pisca
	
	if(count==0 && led==0)   //como count=2, 250ms x2=500ms, meio segundo para apagar, outro para acender, vem para este if se led estiver desligado
	{
		
		PORTD=0b11111111;   //liga led
		count=2;            //reinicia contador
	}

	if(count==0 && led==1)   //ou para este if se o led estiver ligado
	{
		PORTD=0b11111110;     //desliga led
		count=2;              //reinicia contador
	}
}


void main(void)
{
	inic();              //Dá inicio ao inic, logo inicia as configurações
	ADCSRA|=0b01000000;  //começar conversão do ADC

	while(1){  //ciclo infinito para nao ter fim

	}

}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:

TCCR0A =0b00000010;      // Funcionamento CTC, T=Prescaler/CLK *(OCR0 + 1)
	TCCR0B =0b00000101;      // Tempo=250ms, Prescaler=1024, CLK=1MHZ
	TIMSK0 |=0b00000010;     // ativa timer0 compare match A

 

You need to get away from using the "magic numbers" and start using self documenting code, so for the above some thing like this:

    TCCR0A = (1<<WGM01);      // Funcionamento CTC, T=Prescaler/CLK *(OCR0 + 1)
	TCCR0B = (1<<CS02)|(1<<CS00));      // Tempo=250ms, Prescaler=1024, CLK=1MHZ
	TIMSK0 = (1<<OCIE0A);     // ativa timer0 compare match A
	
	

You can also give port pins a name with a define and use that instead of more magic numbers.

 

Jonaas18 wrote:

led=(PORTD & 0b00000001);   //Led que pisca
	
	if(count==0 && led==0)   //como count=2, 250ms x2=500ms, meio segundo para apagar, outro para acender, vem para este if se led estiver desligado
	{
		
		PORTD=0b11111111;   //liga led
		count=2;            //reinicia contador
	}

	if(count==0 && led==1)   //ou para este if se o led estiver ligado
	{
		PORTD=0b11111110;     //desliga led
		count=2;              //reinicia contador
	}

Becomes a more readable:

#define LED1 1

led=(PORTD & LED1);   //Led que pisca
	
	if(count==0 && led==0)   //como count=2, 250ms x2=500ms, meio segundo para apagar, outro para acender, vem para este if se led estiver desligado
	{
		
		PORTD |= LED1;   // liga led
		count=2;            //reinicia contador
	}

	if(count==0 && led==1)   //ou para este if se o led estiver ligado
	{
		PORTD &= ~LED1;     // desliga led
		count=2;              //reinicia contador
	}

 

On the M88 you can also use the pin toggle feature of the PIN register, in stead of the RMW to the PORT register.

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 Besides having to build everything in a breadboard (ATMega328p, the led/fan, etc.)

If you are learning hardware and software at the same time your work is significantly increased compared to focusing on just one at a time.

 

Are you allowed to use a Mega328P that is pre-mounted on a little circuit board that plugs into your breadboard, (i.e. an Arduino Nano, $3 USD) ?

 

If you use the Nano then you have a pre-mounted micro, with a power supply, by-pass caps, a 16 MHz crystal (or resonator...), crystal caps, a reset push button switch, one on-board LED, USB or external power, and a USB data comm's port, (serial port). 

 

ALL of that is already done for you!

 

You can then add one resistor, (330 ohms), and one LED, and your hardware is completed for the project.

 

That allows you to then focus on the software.

 

You can still program the Mega328P in whatever language you wish to use, (C, Asm, Ada, Basic, etc.), you do not have to use the Arduino programming environment or its language.

 

Even if you have to use a "bare micro" on the breadboard, life is much easier if you start your project with a Nano, and get your code up and running.

Then work on wiring up the micro.

If you have a problem then at least you know the problem is with your hardware setup, as you already have it working with the Nano.

 

The photo below shows this concept. 

It is a Nano in a breadboard, along with a GPS module and some other items.

 

JC

 

 

Edit: Typos

 

 

 

 

Last Edited: Wed. Oct 2, 2019 - 02:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Alright. I read what you explained and I decided to get a closer look at the ATMega88 (note that I'm not using the ATMega328 like advertised in the main question) datasheet. I managed to redo the code without the magic numbers (partly) aswell as explain in comments what I'm expecting to happen (why am I setting this bit, or not setting that bit, etc.). At the moment, the code looks like this:

 


#include <avr/interrupt.h>
#include <stdio.h>
#include <avr/io.h>
#define LED 1

volatile unsigned char count = 2;                            // Counter
volatile unsigned char led ;                                // Verifies if LED is turned ON or OFF

void inic(void) {
    
    PORTD = 0b00000011;                                        // Defined PD0 / PD3 as outputs. PD0: Blinking LED ; PD3: LED (pwm) [OC2B].
    DDRD = 0b00000011;

    TCCR0A = (1<<WGM01);                                    // COM0Ax: Normal port operation ; COM0Bx: Normal port operation ; Bits 3&2: Reserved and read as zero ; CTC mode (T = [Prescaler/Clock] * (OCR0 + 1)
    TCCR0B = (1<<CS02)|(1<<CS00);                            // FOC0x: Read as zero ; Bits 5&4: Reserved and read as zero ; Prescaler = 1024 & Clock = 1Mhz
    TIMSK0 = (1<<OCIE0A);                                    // Timer/Counter0 Compare Match A interrupt enabled
    OCR0A = 243;                                            // 0,245 = [1024/1MHz] * (OCR0 + 1) <=> OCR0 = 243 (0,249 ms)

    TCCR2A = (1<<COM2B1) | (1<<WGM21) | (1<<WGM20);            // COM2Ax: Normal port operation ; COM2Bx: Clear OC2B on Compare Match & Set OC2B at Bottom ; Bits 3&2: Reserved and read as zero ; Fast PWM
    TCCR2B = (1<<CS21)|(1<<CS20);                            // FOC2x: Read as zero ; Bits 5&4; Reserved and read as zero ; Prescaler = 32 & Clock = 1Mhz
    OCR2B = 0;                                                

    ADMUX =    (1<<REFS0)|(1<<ADLAR)|(1<<MUX2)|(1<<MUX0);        // External capacitor on AREF pin (potentiometer) ; ADLAR adjusted to the left: Bit = 1 ; Bit 4 ; Reserved and read as zero ; PC5 (ADC5) defined as input
    ADCSRA = (1<<ADEN)|(1<<ADIE)|(1<<ADPS1)|(1<<ADPS2);     // ADEN enabled: Bit = 1 ; ADSC set when converting ; ADATE: Disabled auto triggering of the ADC ; ADIF: Set when conversion completes ; Prescaler = 8

    sei();                                                    // global int enable
}

ISR(ADC_vect) {                                                // ADC interruption
    
    OCR2B = ADCH;                                            // Transfers the value of the potentiometer to the led on PD3
    ADCSRA |= (1<<ADSC);                                    // Starts the conversion
}

ISR(TIMER0_COMPA_vect) {                                    // Led blink interruption
    
    count--;                                                // Counts 250ms
    led = (PORTD & LED);                                    // Blinking LED
    
    if (count==0 && led==0) {                                // Count = 2 by default. 250ms * 2 = 500ms. Half a second on, Half a second off. Comes here if led is turned off
        PORTD |= LED;                                        // LED ON
        count = 2;                                            // Reset counter
    }

    if (count==0 && led==1) {                                //Comes here if led is turned on
        PORTD &= ~LED;                                        // LED OFF
        count = 2;                                            // Resets counter
    }
}

void main(void) {
    inic();                                                    // Calls the inic function
    
    ADCSRA |= (1<<ADSC);                                    //Starts the ADC conversion

    while(1){                                                //infinite cycle
    }

}

 

I have some questions though:

 

  1. I wrote OCR2B = 0; without knowing the formula (I took it from a work I made last year). I searched for a formula near the Fast PWM section but could only find one that calculates the PWM frequency (Clock/Prescaler*256). Why is OCR2B = 0?
  2. In your reply you wrote: 
    PORTD |= LED1;

    and 

    PORTD &= ~LED1;

    Is LED1 a typo or is it supposed to be like that? The variable is called LED in the define function at the top of the program. I wrote LED only in my code, as you can see.

  3. Is this right? 
        while(1){                                                //infinite cycle
        }
  4. So far, am I any near of my objectives? (Blinking a LED - 500ms ON and 500ms OFF) (Changing a LED brightness via PWM) (Having a 1Hz wave on the output of my atmega).

 

EDIT1: This is the FAST PWM Mode section on the datasheet. There's this formula but I have no idea what it has to do with OCR2B. Also, I end up with ~120Hz at the end of this equation. What's this for / does it have any purpose for this project?

 

 

Last Edited: Wed. Oct 2, 2019 - 04:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I really don't know whether I can use it or not. I've used that "board" before with everything connected inside when I learned Assembly / C on ATMegas. I don't think I can use that here and that's really one of my biggest issues. I had no idea how to connect a led to the pins and how to program the ATMega (with that board I only had to connect a USB cable into my PC and click send or whatever the option was to my ATMega and everything would be fine). In this case, I must build the circuit alone (which is simple although I'm still trying to figure out resistance values and such - I have no idea how to do it since I haven't even thought about it ; focusing on the code part now) aswell as send the code into my micro via a programmer (the same USB thing I see at the right of the picture you've sent).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From what I've discussed with my teacher, the code is pretty much ok besides missing the 1 Hz wave part. I'm also not sure if I've coded everything there is to code about the PWM section but I'll test it on tuesday.