cemox
cemox

Reputation: 21

Can't update Output Compare Register (OCRnA) via USART (Atmega88)

I managed to create a perfect square wave by using 8-bit timer2 in atmega88 running @ 16MHz. I also managed the change the OCR2A in the code and change the frequency of the signal as expected. I checked everything with oscilloscope and no problem there.

However, I want to change the OCR2A (i.e. change the frequency) by sending one byte data to the Atmega via USART. When I do that I observe seemingly random frequency change.

I am using Cutecom in HEX mode and I also added a piece of code to check whether the data byte received is correct. (Toggle a LED if the received byte is 40). It shows that data received correctly. I also avoid using timer based delays function and timer0; I am using timer2 (and software delay function (_delay_ms) if I have to).

But, still cannot succeed. The original usart_receive function returned unsigned char, it did not work either.

Initial value of the OCRA is 38 and it produces perfect wave at 3205 Hz as expected. When I send 40 serially (0x28) the LED blinks confirming that the decimal 40 received. The frequency changes not to 3048 Hz as expected but to some strange value. When I send 38 (0x26) to atmega again, it does not go back to the initial frequency (i.e. 3205 Hz). (disregard blink function in the code).

Any help will be highly appreciated.

#include <avr/io.h>
#include <util/delay.h>
#include <stdbool.h>

#define FOSC 16000000 // Clock Speed
#define BAUD 19200
#define MYUBRR FOSC / 16 / BAUD - 1

#define CLR(x, y) (x &= (~(1 << y)))
#define SET(x, y) (x |= (1 << y))

#define maxOCR2A 255
#define minOCR2A 15
#define waitTime 20

int USART_Receive(void);
void USART_Init(unsigned int ubrr);
void blink(char x);

void loop(void);

int main()
{

    USART_Init(MYUBRR);

    // led
    DDRB |= _BV(PORTB0);
    blink(3);

    // OC2A pin
    DDRB |= _BV(PORTB3);
    _delay_ms(150);

    TCCR2B = 0; // reset timer 0 control registers
    TCCR2A = 0;

    // Toggle OC0A on compare match, (CTC)
    TCCR2A = (1 << COM2A0) | (1 << WGM21);
    // Start the timer, x prescalar
    TCCR2B = (1 << CS22); // | (1 << CS20); // prescale 64
    //  TCCR0B = (1 << CS01); // prescale 8
    OCR2A = 38;

    loop();
}

void loop(void)
{

    int t;
    while (true)
    {
        t = USART_Receive();
        if(t == 40) PORTB ^= _BV(PORTB0);;
        TCNT2 = 0;
        OCR2A = t;

    }
}

int USART_Receive(void)
{
    /* Wait for data to be received */
    while (!(UCSR0A & (1 << RXC0)))
        ;
    /* Get and return received data from buffer */
    return UDR0;
}

void USART_Init(unsigned int ubrr)
{
    /*Set baud rate */
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)ubrr;
    /* Enable receiver and transmitter */
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);
    /* Set frame format: 8data, 1stop bit */
    UCSR0C = ((1 << UCSZ01) | (1 << UCSZ00));
}

void blink(char x)
{
    for (char i = 0; i < x; i++)
    {
        SET(PORTB, PORTB0);
        _delay_ms(200);
        CLR(PORTB, PORTB0);
        _delay_ms(200);
    }
}

Upvotes: 0

Views: 48

Answers (1)

cemox
cemox

Reputation: 21

Solved it by replacing usart_receive with interrupt version.

ISR(USART_RX_vect)
{
    PORTB ^= _BV(PORTB0);
    OCR2A = UDR0;
    
}

Upvotes: 0

Related Questions