Brushed ESC with Attiny45

Liebe Forengemeinde,

Für unsere ferngesteuerten LEGO® Autos habe ich eine bidirektionale Motorsteuerung mit einem ATtiny45 und dem Motortreiber RZ7886 entwickelt. Der Tiny45 soll die Pulsbreite vom Empfänger auswerten und ein PWM Signal – je nach Fahrtrichtung und Geschwindigkeit – an den Motortreiber ausgeben.

Die Pulsbreite liegt üblicherweise zwischen 1000 und 2000 µs, kann aber je nach Fernbedienung leicht darüber oder darunter liegen.

Aus irgendeinem Grund, der sich mir nicht erschließt fängt der Motor bei Erreichen des max. Wertes für ‚Rückwärts‘ - im Sketch die 1050 - an zu Stottern. Dieses Stottern hört auf, wenn man den Hebel der Fernbedienung ein kleines Stück zurück oder noch ein Stück weiter bewegt.

Auf einem ATmega328pb funktioniert der Sketch einwandfrei.

Habt ihr eine Idee woran das liegt, dass der Tiny45 das nicht so macht?

/*
   Using ATtiny45 @8MHz on AUBR® micro board
   Hyperbolic Drive Control and Channel 3 for switching Lights
*/



#include <PinChangeInterrupt.h>

#define DRIVE_OUT1  0  //   9
#define DRIVE_OUT2  1  //  10
#define DRIVE_IN    2  //  22   PCINT#
#define LIGHTS_IN   4
#define LIGHTS_OUT  3



unsigned int driveOutput   = 0;
unsigned int driveMap      = 0;

unsigned int  lightsInput  = 0;
bool lightsOn              = false;

volatile unsigned int pulseDuration   = 0;
volatile unsigned int risingTime      = 0;

//************************************************************
void setup() {
  pinMode( DRIVE_OUT2, OUTPUT );
  pinMode( DRIVE_OUT1, OUTPUT );
  pinMode( LIGHTS_OUT, OUTPUT );

  attachPCINT ( DRIVE_IN, rising, RISING );
}

//************************************************************
void loop() {
  lightsInput = pulseIn( LIGHTS_IN, HIGH );

  if ( lightsInput > 1400 ) PORTB |=  ( 1 << 3 );
  if ( lightsInput < 1400 ) PORTB &= ~ ( 1 << 3 );

  /*
    if ( lightsInput > 1400 && !lightsOn ) {
    //digitalWrite( LIGHTS_OUT, HIGH );
    PORTB |= ( 1 << 3 );
    lightsOn = true;
    }

    if ( lightsInput < 1400 && lightsOn ) {
    // digitalWrite( LIGHTS_OUT, LOW );
    PORTB &= ~( 1 << 3 );
    lightsOn = false;
    }
  */
}

//************************************************************
//************************************************************
void rising() {
  attachPCINT ( DRIVE_IN, falling, FALLING );
  risingTime = micros();
}

void falling() {
  attachPCINT ( DRIVE_IN, rising, RISING );
  pulseDuration = micros() - risingTime;
  drive( pulseDuration );

}

void drive( unsigned int driveInput ) {
  if ( driveInput > 2500 || driveInput < 500 ) driveInput = 1500;
  //constrain ( driveInput , 900, 2000 );

  //  vorwärts
  if ( driveInput > 1500 )   {
    driveMap = map( driveInput, 1500, 1950, 0, 255 );
    if ( driveMap > 255 ) driveMap = 255;
    driveOutput = sq( driveMap ) / 255;
    digitalWrite( DRIVE_OUT1, LOW );
    analogWrite ( DRIVE_OUT2, driveOutput );
  }

  //  rückwärts
  else if (driveInput < 1500) {
    driveMap = map( driveInput, 1500, 1050, 0, 255 );
    if ( driveMap > 255 ) driveMap = 255;
    driveOutput = sq( driveMap ) / 255;
    digitalWrite( DRIVE_OUT2, LOW );
    analogWrite ( DRIVE_OUT1, driveOutput );
  }

  else {
    digitalWrite( DRIVE_OUT1, LOW );
    digitalWrite( DRIVE_OUT2, LOW );
  }
}
//************************************************************
//************************************************************

I am surprised it works at all, as micros() is defined as unsigned long. You seem to capture it in a unsigned int.
risingTime = micros();

A while ago when I had to capture a servo signal on a Attiny, I remember I had to deal with clashing interrupts. While being In the pin change ISR the timer used for calculating the pulselength (you use micros() for that) was overflowing, but the overflow was only seen after I was leaving the pin change ISR.
Pin change ISR have higher priority than Timer Overflow ISR. And with the 8 bit timer on the Attiny45 you will have more of them than the 16 bit timers of the Atmega328PB

And since you are using a pinchangeinterrupt library it is difficult to tell how that works together with micros() timekeeping.

I could then only get it to work in the pin change ISR by testing myself if the overflow interrupt flag was already set by the timer, so I knew I had to add 255 more timer ticks to calculate the right pulselength.

Dear hmeijdam,

many thanksfor your reply!

Yes the micros() are captured as an unsigned int. I also tried long and unsigned long. But, this did not chage the behavior of the chip.

If I got you right, does an overflow shoulnd't apear on the higer end (ie @ 2000µs) instead of on the lower end (ie @1000µs ) of a pulse length?

I also tried without improvement:

  • pulseIn() to capture the pulse length
  • running the tiny on 16Mhz internal
  • other PWM output pin for reardrive ( PB4 )
  • swichting the output pins

Also noticed, that the headlights on the car are flickering when the car starts struggeling when driving backwards ( forwards when the ouputpins are switched ).
Using a lower Pulselength for switching the lights prevents that flicker.

You may want to research your power distribution to the motor, or replace the motor with some leds to test if induction spikes from the motor interfere. When I hook up your setup on a breadboard with an Attiny85 and 3 leds the forward/backwards PWM and operating the lights works smooth over the whole servo pulse range. No glitches to be seen.

Dear hmeijdam,

many thanks for verifying the code! And yes, there were interferences on the line between the tiny and the motor driver, occurring when the PB0 pin switches from PWM to constant High.
I modified some boards by cutting the two lines between the chips and placed RC elements there. Now the guy is running as expected!

Best

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.