Fast PWM 1kHz | 6V DC engine control using RC receiver || Attiny 2313

64 Views Asked by At

This is my program which doesn't working correctly.

MCU, regardless of the position of the lever on the remote control, sends a signal to pin D PB2 4.7V. On other pins A PB3, B PB2, C PD5 is 0V. Engine isn't running.

Link to the datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/doc8246.pdf

  • TCCR0A form 82 page
  • TCCR1A form 111 page

How should I set the registers to generate PWM signal on pins?

  • PB3 + PB2 Engine turn left
  • PB4 + PC5 Engine turn right
  • RC receiver sends PWM signal to MCU on PD2 pin.

I'm writing program in Microchip Studio to control DC 6V engine (graupner 400 max 4,0A) using RC radio receiver.

I'm using Attiny 2313A with F_CPU 16 MHz.

To control motor I would like to generate a PWM signal with a frequency of 1 kHz.

Thanks a lot.

#define F_CPU 16000000L
#define MCU attiny2313
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#define A PB3
#define D PB2
#define B PB4
#define C PD5
volatile uint16_t R = 0;
volatile uint8_t R0 = 0;
volatile uint16_t R1 = 0;
void measurePWM() {
    TCNT1 = 0;
    while (!(PIND & (1 << PD2)));
    while (PIND & (1 << PD2));
    R = TCNT1;
}
int main(void) {
TCCR0A = (1 << COM0A1) | (1 << WGM02) | (1 << WGM01) | (1 << WGM00);
TCCR1A = (1 << COM1A1) | (1 << WGM12) | (1 << WGM10);
TCCR0B = (1 << COM0B1) | (1 << WGM02) | (1 << WGM01) | (1 << WGM00) | (1 << CS01);
TCCR1B = (1 << COM1B1) | (1 << WGM12) | (1 << WGM10);
    DDRB |= (1 << DDB2) | (1 << DDB3) | (1 << DDB4);
    DDRD |= (1 << DDD5);
    PORTB &= ~((1 << A) | (1 << D) | (1 << B));
    PORTD &= ~(1 << C);
    DDRD &= ~(1 << PD2);
    PORTD &= ~(1 << PD2);
    while (1) {
        measurePWM();
        R0 = (R - 958) * 255 / (1815 - 958);
        R1 = (R - 958) * 65534 / (1815 - 958);
        if (R >= 1500) {
            OCR0A = 0;
            OCR1A = 0;
            OCR0B = R0;
            OCR1B = R1;
            } else if (R <= 1300) {
            OCR0A = 0;
            OCR1A = 0;
            OCR0B = R0;
            OCR1B = R1;
            } else {
            OCR0A = 0;
            OCR0B = 0;
            OCR1A = 0;
            OCR1B = 0;
        }
        _delay_ms(100);
    }
    return 0;
}
1

There are 1 best solutions below

0
sunriax On

If you want to generate a PWM, you can use the timer for it. Check the datasheet of Tiny2313A. The block diagram on page 71 shows that there is already a waveform generation mode integrated in timer0. If you want to use other pins it is necessary to use timer interrupts.

The timer supports two modes for PWM generation

  • FastPWM (Page 77)
  • Phase correct PWM (Page 79)

A simple example to generate a waveform at around ~1kHz with integrated PWM modes:

The PWM Frequency for FastPWM can be calculated with enter image description here. For your system clock speed we get with a prescaler of 64 (N) a frequency f_PWM of ~976 Hz.

#define F_CPU 16000000UL

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

ISR(TIMER0_OVF)
{
  PORTB |= (1<<PB2);
}

ISR(TIMER0_OCR0A)
{
  PORTB &= ~(1<<PB2);
}

int main(void)
{
  // Timer initialisation:
  TCCR0A = (1<<WGM01) | (1<<WGM00);  // For FastPWM
  OCR0A = 127;                       // For 50% duty cycle
  TIMSK = (1<<TOIE0) | (1<<OCIE0A);  // Enable timer overflow and ocr register overflow interrupt

  TCCR0B = (1<<CS01) | (1<<CS00);    // For presacler 64

  // Port setup
  DDRB = (1<<PB2);

  // Enable interrupts globally
  sei();

  while(1)
  {
    // Do something that is not time critical...
  }
}

Now it is possible to control the duty cycle with OCR0A register.

If the OC pins of the microcontroller are used it is not necessary to use the ISRs because the timer itself sets the OC pins. You have already used that in your example.

If you want to measure an external pwm clock cycle it is necessary to extend the program with an external interrupt. A predefined pin triggers the external interrupt. With the status of the pin and with an additional timer it is possible to measure the current duty cycle e.g:

#define F_CPU 16000000UL

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

volatile unsigned int count = 0;
volatile unsigned int duty_cycle = 0;

ISR(INT0_vect)
{
  if(ISR PIN is HIGH)
  {
    // Calculate duty cycle in %
    duty_cycle = count * 100 / temp;

    // Reset Timer
    TCNT0 = 0;
  }
  else
  {
    count = TCNT0
  }
}

int main(void)
{
  // Enable external interrupt:
  MCUCR = (1<<ISC00);  // Any logical change will call the interrupt
  GIMSK = (1<<INT0);   // Enable external interrupt 0

  // Timer initialisation:
  TCCR0B = (1<<CS01) | (1<<CS00);    // For presacler 64
  // The lower the prescaler gets the higher the precision of the measurement. It is necessary that the datatype becomes no overflow (unsigned int -> max. value 65535)

  sei();  // Enable interrupts globally

  while(1)
  {
    // Do something that is not time critical...
  }
}

Maybe this helps. Currently i have no platform to test...