Brushed ESC mit Afro20A BL-ESC und arduino IDE

cesco1

Erfahrener Benutzer
#1
Ich brauche eine brushed regler. Der früher gekaufte turnigy ist leider unbrauchbar, zu niedrige pwm frequenz und nur eine laufrichtung. Ich kann mich aber erinnern ein cleverer typ hat mal eine BL regler für brushed missbraucht. Ich hab danach gesucht aber nix mehr gefunden.

Beim herumstöbern hab ich eine alte afro 20A esc gefunden. Bei denen sind seriell RX und TX herausgeführt. Das, und reset ist das einzige was die Arduino IDE braucht. Ein schneller check und ja, arduino unterstüzt den atmega8. Also schnell probieren.

Board als "Arduino NW w. Atmega8" einstellen. Zuerst "programmer - uabasp" einstellen. Arduino bootloader über USBASP flashen. Das findet man unter "tools - bootloader installieren". Das dauert dann etwas.

Dann afro esc mit seriall adapter verbinden. gnd auf gnd, rx auf tx, tx auf rx. Man kann den reset pin über einen kondensator mit DTR verbinden, muss aber nicht, man kann manuell resetten. Der atmega8 bootloader wartet ganze 10 sekunden. Zum testen afro esc einschalten und dann innerhalb 10 sekunden den "upload" button klicken. Funktioniert. Programmstart ist 10 sek verzögert.

Das programm hab ich nicht fertig ... ist aber einfach. Im gegensatz zu den BL firmwares möchte ich hardware pwm verwenden. Konze-bldc, Simonk und Blheli-atmel verwenden alle soft-pwm. Igitt und warum auch (weil der mega8 dazu nicht taugt, 328 ginge das). Glücklicherweise sind 2 fet eingänge schon mit pwm fähigen ausgängen verbunden, nämlich BpFET, pin10, OC1B und CpFET, pin9, OC1A. Dazu noch BnFET und CnFET als statische schalter und fertig ist die H brücke.

Unter verwendung von ApFET und AnFET wäre sogar eine dual-esc möglich, z.b. für modellpanzer.

Testcode zur ansteuerung hab ich, viel weiter bin ich noch nicht. Anregungen / kommentare ?
 
Zuletzt bearbeitet:

cesco1

Erfahrener Benutzer
#2
Hier die source zum spielen:
Code:
#include <Arduino.h>

#define MIDRC 1500
#define DEADZONE 50

// BpFET		= 2 pin10 PB2 - oc1b
// CpFET		= 1 pin9  PB1 - oc1a
// CnFET		= 5 pin5  PD5
// BnFET		= 4 pin4  PD4

#define BpFET 10
#define CpFET 9
#define BnFET 4
#define CnFET 5
#define IN_PIN 8 // PB0, ICP

volatile uint16_t rcValue = 1500;
volatile uint16_t edgeTime;
volatile boolean rxFrameDone;

const uint16_t llim = 50;

ISR(TIMER1_CAPT_vect) 
{
  uint16_t now,diff;
  now = micros();
  diff = now - edgeTime;
  if (diff>3000) 
  {
    edgeTime = now;
    TCCR1B &= ~(1<<ICES1);
  }
  else if (950<diff && diff<2050) 
  {
    rcValue = diff;
    TCCR1B |= (1<<ICES1);
    rxFrameDone = true;
  }
}

void setup() 
{ 
  pinMode(BnFET, OUTPUT);  
  pinMode(BpFET, OUTPUT);  
  pinMode(CnFET, OUTPUT);  
  pinMode(CpFET, OUTPUT);  
  
  Serial.begin(9600); 
  Serial.println("AfroEscTest"); 
  
  TCCR1A = (1<<WGM11);
  TCCR1B = (1<<CS11);
  TCCR1A |= (1<<COM1A1) | (1<<COM1A0); // connect pin  9 to timer 1 channel A
  TCCR1A |= (1<<COM1B1) | (1<<COM1B0); // connect pin 10 to timer 1 channel B   
  OCR1A = 0x00;
  OCR1B = 0x00; 
  
  // setup rx
  TCCR1B |= (1 << ICES1 | 1<<ICNC1);
  TIMSK |= (1 << TICIE1); 
  pinMode (IN_PIN, INPUT);
} 

void writemotfwd(uint16_t val)
{
  digitalWrite(CnFET, LOW);
  OCR1B = 0; 
  OCR1A = val;
  digitalWrite(BnFET, HIGH);
}

void writemotrev(uint16_t val)
{
  digitalWrite(BnFET, LOW);
  OCR1A = 0; 
  OCR1B = val;
  digitalWrite(CnFET, HIGH);
}

uint8_t skipct = 0;

void loop() 
{ 
  if (rxFrameDone)
  {
    uint16_t lev;
    int16_t val;
    rxFrameDone = false;
    Serial.println(rcValue); 
    if (rcValue > MIDRC)
    {
      val = rcValue - MIDRC - DEADZONE;
      lev = constrain(val,0,510);
      writemotfwd(lev);
      //Serial.print("RC+ "); 
    }
    else
    {
      val = MIDRC - DEADZONE - rcValue;
      lev = constrain(val,0,510);
      writemotrev(lev);
      //Serial.print("RC- "); 
    }
    //Serial.println(lev); 
    skipct = 0;
  }
  else 
  { 
    if (skipct < 20) skipct++;
    else 
    {
      writemotfwd(0);
      skipct = 0;
      Serial.print('.');
    }
  }
  delay(10);
}
Der rx teil ist etwas würg weil int0 oder int1 nicht verwendbar.
 
Zuletzt bearbeitet:

cesco1

Erfahrener Benutzer
#3
Da das ganze mit arduino läuft will ich das noch mit avrstudio versuchen damit ich später auch übriggebliebene esc anderer bauart verwenden kann. Aber ich war zu faul das 6 adrige ISP kabel zum usbasp wieder anzulöten. Ich habe gefunden dass man mit avrdude auch den arduino bootlader verwenden kann. Genial einfach.
Dieses bat file brauchts:
Code:
avrdude -c avrdude.conf -v -v -v -v -patmega8 -carduino -P\\.\COM5 -b19200 -D -Uflash:w:default/afrobrushed.hex:i 
sleep 25
Natürlich comport und ort der .hex anpassen, sowie die avrdude.exe und avrdude.conf am selben ort haben, und das geht :)

Die avrstudio source für brushed mod auf afro 20A esc:
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#define	F_CPU		 16000000
#include <util/delay.h>


// port B
#define	BpFET		 2	//   PB2 - oc1b
#define	CpFET		 1	//   PB1 - oc1a
#define	INIT_PB		 (1<<BpFET)+(1<<CpFET)
#define	DIR_PB		 (1<<BpFET)+(1<<CpFET)

// port C
#define	red_led		 3
#define	green_led	 2	
#define	INIT_PC		 0
#define	DIR_PC		 (1<<red_led)

#define RED_off   PORTC |=   _BV(red_led)
#define RED_on    PORTC &= ~(_BV(red_led))

// port D
#define	CnFET		 5
#define	BnFET		 4
#define	TxD 		 1
#define	INIT_PD		 0
#define	DIR_PD		 (1<<BnFET)+(1<<CnFET)+(1<<TxD)
  
#define CnFET_on    PORTD |=   _BV(CnFET)
#define CnFET_off   PORTD &= ~(_BV(CnFET))
#define BnFET_on    PORTD |=   _BV(BnFET)
#define BnFET_off   PORTD &= ~(_BV(BnFET))

#define IN_PIN 8 // PB0, ICP
#define BAUD 9600
#define MIDRC 1500
#define DEADZONE 50

volatile uint8_t timect;

ISR(TIMER0_OVF_vect) 
{
  timect++; 
}

volatile uint16_t rcValue;
volatile uint8_t rxFrameDone;
volatile uint16_t edgeTime;
volatile uint8_t phase;

ISR(TIMER1_CAPT_vect) 
{
  uint16_t now;
  now = (timect<<8) + TCNT0; // 1ms=256, 2ms=512
  if (phase == 1) // pos edge
  {
    TCCR1B &= ~(1<<ICES1);
    phase = 0;
	edgeTime = now;
  }
  else // neg edge
  {
	TCCR1B |= (1<<ICES1);
    phase = 1;
    rcValue = (now - edgeTime)<<2;    
    rxFrameDone = 1;
  }
}

void putch(char c)
{
  UDR = c;
}

void writemotfwd(uint16_t val)
{
  CnFET_off;
  OCR1B = 0;   //BpFET
  OCR1A = val; //CpFET
  BnFET_on;
}

void writemotrev(uint16_t val)
{
  BnFET_off;
  OCR1A = 0;   //CpFET
  OCR1B = val; //BpFET
  CnFET_on;
}

//************************************************************
// Main loop
//************************************************************

uint8_t skipct = 0;

int main(void)
{
  int16_t val;

  // Main loop

  _delay_ms(10.0);
  _delay_us(10.0);

  PORTB = INIT_PB;
  DDRB  = DIR_PB;
  PORTC = INIT_PC;
  DDRC  = DIR_PC;
  PORTD = INIT_PD;
  DDRD  = DIR_PD;

  // setup time base
  TCCR0 = (1<<CS01) | (1<<CS00); // 250 ticks per sec
  TIMSK = (1<<TOIE0);

  // setup pwm output
  TCCR1A = (1<<WGM11);
  TCCR1B = (1<<CS11);
  TCCR1A |= (1<<COM1A1) | (1<<COM1A0); // connect pin  9 to timer 1 channel A
  TCCR1A |= (1<<COM1B1) | (1<<COM1B0); // connect pin 10 to timer 1 channel B   
  OCR1A = 0x00;
  OCR1B = 0x00; 
  
  // setup rx
  TCCR1B |= (1 << ICES1 | 1<<ICNC1);
  TIMSK  |= (1 << TICIE1);

  // setup serial
  uint8_t h = ((F_CPU  / 4 / BAUD -1) / 2) >> 8;
  uint8_t l = ((F_CPU  / 4 / BAUD -1) / 2);
  UCSRA  = (1<<U2X);
  UCSRB  = (1<<TXEN);
  UBRRH  = h; 
  UBRRL  = l; 

  sei();

  while (1)
  {
    
    if (rxFrameDone == 1)
	{
	  rxFrameDone = 0;
	  RED_on;
	  skipct = 0;

      if (rcValue > MIDRC)
      {
        val = rcValue - MIDRC - DEADZONE;
		if (val <= 0) val = 0;
		else if (val >= 510) val = 510;
        writemotfwd(val);
      }
      else
      {
        val = MIDRC - DEADZONE - rcValue;
		if (val <= 0) val = 0;
		else if (val >= 510) val = 510;
        writemotrev(val);
      }
	}
	else
	{
      if (skipct < 20) skipct++;
      else 
      {
        writemotfwd(0);
        skipct = 0;
	    RED_off;
      }
	}    

    _delay_ms(10.0);
  }
}
Die avrstudio source für brushed mod auf DYS_16A (die 1 eur dinger) :

Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#define	F_CPU		 16000000
#include <util/delay.h>


// port B
#define	CnFET		 1	//   PB1 - oc1a
#define	INIT_PB		 0
#define	DIR_PB		 (1<<CnFET)

// port C
#define	INIT_PC		 0
#define	DIR_PC		 0

// port D
#define	BnFET		 7
#define	AnFET		 5
#define	ApFET		 4
#define	BpFET		 3
#define	CpFET		 2
#define	TxD 		 1
#define	RxD 		 0
#define	INIT_PD		 (1<<ApFET)+(1<<BpFET)+(1<<CpFET)
#define	DIR_PD		 (1<<AnFET)+(1<<BnFET)+(1<<ApFET)+(1<<BpFET)+(1<<CpFET)

#define AnFET_on    PORTD |=   _BV(AnFET)
#define AnFET_off   PORTD &= ~(_BV(AnFET))
#define BnFET_on    PORTD |=   _BV(BnFET)
#define BnFET_off   PORTD &= ~(_BV(BnFET))
#define CnFET_on    PORTB |=   _BV(CnFET)
#define CnFET_off   PORTB &= ~(_BV(CnFET))

#define ApFET_on    PORTD |=   _BV(ApFET)
#define ApFET_off   PORTD &= ~(_BV(ApFET))
#define BpFET_on    PORTD |=   _BV(BpFET)
#define BpFET_off   PORTD &= ~(_BV(BpFET))
#define CpFET_on    PORTD |=   _BV(CpFET)
#define CpFET_off   PORTD &= ~(_BV(CpFET))

#define MIDRC 1500
#define IRLAT 20


volatile uint8_t fwddir;

ISR(TIMER2_OVF_vect) 
{
  ApFET_on;
  CpFET_on;
}

ISR(TIMER2_COMP_vect) 
{
  if      (fwddir==0) ApFET_off;
  else if (fwddir==1) CpFET_off;
}

volatile uint16_t rcValue;
volatile uint16_t edgeTime;
volatile uint8_t rxFrameDone;
volatile uint8_t phase;

ISR(TIMER1_CAPT_vect) 
{
  uint16_t now;
  now = ICR1; 
  if (phase == 1) // pos edge
  {
    TCCR1B &= ~(1<<ICES1);
    phase = 0;
    edgeTime = now;
  }
  else // neg edge
  {
    TCCR1B |= (1<<ICES1);
    phase = 1;
    rcValue = (now - edgeTime)>>1;    
    rxFrameDone = 1;
  }
}

void writemot(uint16_t val)
{
  // switch cp, cplo and bnhi = current
  if (fwddir==0)
  {
    AnFET_off;
    CnFET_on;
  } 
  else 
  {
    CnFET_off;
    AnFET_on; 
  }
  if (val >= 255) val = 255;
  if (val < IRLAT) fwddir = 3;
  else OCR2 = 255-val;
}

//************************************************************
// Main loop
//************************************************************

uint8_t skipct = 0;

int main(void)
{
  int16_t val;

  // Main loop

  PORTB = INIT_PB;
  DDRB  = DIR_PB;
  PORTC = INIT_PC;
  DDRC  = DIR_PC;
  PORTD = INIT_PD;
  DDRD  = DIR_PD;

  // setup pwm
  TCCR2  = (1 << WGM21) | (1<<WGM20);
  TCCR2 |= (1 << CS21)  | (1 << CS20);
  OCR2   = 0x80;
  TIMSK |= (1 << OCIE2) | (1 << TOIE2); 

  // setup timer
  TCCR1A = 0;
  TCCR1B = (1<<CS11);
  TCCR1B |= (1 << ICES1 | 1<<ICNC1);
  TIMSK |= (1 << TICIE1); 
   
  sei();

  while (1)
  {
    if (rxFrameDone == 1)
	{
	  rxFrameDone = 0;
	  skipct = 0;

      if (rcValue > MIDRC)
      {
        val = rcValue - MIDRC;
		fwddir = 1;
      }
      else
      {
        val = MIDRC - rcValue;
		fwddir = 0;
      }
	  writemot(val>>1);
	}
	else
	{
      if (skipct < 20) skipct++;
      else 
      {
        writemot(0);
        skipct = 0;
      }
	}

    _delay_ms(100.0);

  }
}
Die avrstudio source für brushed mod auf HK20A, bs_nfet:

Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#define	F_CPU		 16000000
#include <util/delay.h>


// port B
#define	CnFET		 0	
#define	INIT_PB		 0
#define	DIR_PB		 (1<<CnFET)

// port C
#define	BpFET		 5
#define	BnFET		 4
#define	CpFET		 3
#define	INIT_PC		 (1<<BpFET)+(1<<CpFET)
#define	DIR_PC		 (1<<BnFET)+(1<<BpFET)+(1<<CpFET)

// port D
#define	AnFET		 5
#define	ApFET		 4
#define	TxD 		 1
#define	RxD 		 0
#define	INIT_PD		 (1<<ApFET)
#define	DIR_PD		 (1<<AnFET)+(1<<ApFET)

#define AnFET_on    PORTD |=   _BV(AnFET)
#define AnFET_off   PORTD &= ~(_BV(AnFET))
#define BnFET_on    PORTC |=   _BV(BnFET)
#define BnFET_off   PORTC &= ~(_BV(BnFET))
#define CnFET_on    PORTB |=   _BV(CnFET)
#define CnFET_off   PORTB &= ~(_BV(CnFET))

#define ApFET_on    PORTD |=   _BV(ApFET)
#define ApFET_off   PORTD &= ~(_BV(ApFET))
#define BpFET_on    PORTC |=   _BV(BpFET)
#define BpFET_off   PORTC &= ~(_BV(BpFET))
#define CpFET_on    PORTC |=   _BV(CpFET)
#define CpFET_off   PORTC &= ~(_BV(CpFET))

#define MIDRC 1500
#define IRLAT 20


volatile uint8_t fwddir;

ISR(TIMER2_OVF_vect) 
{
  ApFET_on;
  CpFET_on;
}

ISR(TIMER2_COMP_vect) 
{
  if      (fwddir==0) ApFET_off;
  else if (fwddir==1) CpFET_off;
}

// input pin D2 int0
volatile uint16_t rcValue;
volatile uint16_t edgeTime;
volatile uint8_t rxFrameDone;

ISR(INT0_vect) 
{
  uint16_t now,diff;
  now = TCNT1; 
  diff = now - edgeTime;
  if (diff>6000) edgeTime = now;
  else if (2*950<diff && diff<2*2050)
  {
    rcValue = (now - edgeTime)>>1;    
    rxFrameDone = 1;
  }
}

void writemot(uint16_t val)
{
  // switch cp, cplo and bnhi = current
  if (fwddir==0)
  {
    AnFET_off;
    CnFET_on;
  } 
  else 
  {
    CnFET_off;
    AnFET_on; 
  }
  if (val >= 255) val = 255;
  if (val < IRLAT) fwddir = 3;
  else OCR2 = 255-val;
}

//************************************************************
// Main loop
//************************************************************

uint8_t skipct = 0;

int main(void)
{
  int16_t val;

  // Main loop

  PORTB = INIT_PB;
  DDRB  = DIR_PB;
  PORTC = INIT_PC;
  DDRC  = DIR_PC;
  PORTD = INIT_PD;
  DDRD  = DIR_PD;

  // setup pwm
  TCCR2  = (1 << WGM21) | (1<<WGM20);
  TCCR2 |= (1 << CS21)  | (1 << CS20);
  OCR2   = 0x80;
  TIMSK |= (1 << OCIE2) | (1 << TOIE2); 

  // setup timer
  TCCR1A = 0;
  TCCR1B = (1<<CS11);

  // setup int0
  MCUCR  = (1 << ISC00);
  GICR   = (1 << INT0); 
   
  sei();

  while (1)
  {
    if (rxFrameDone == 1)
	{
	  rxFrameDone = 0;
	  skipct = 0;

      if (rcValue > MIDRC)
      {
        val = rcValue - MIDRC;
		fwddir = 1;
      }
      else
      {
        val = MIDRC - rcValue;
		fwddir = 0;
      }
	  writemot(val>>1);
	}
	else
	{
      if (skipct < 20) skipct++;
      else 
      {
        writemot(0);
        skipct = 0;
      }
	}
    
    _delay_ms(10.0);

  }
}
TL,DR: Avrstudio hex mit arduino bootloader hochladen, brushed avrstudio source
 
Zuletzt bearbeitet:

cesco1

Erfahrener Benutzer
#4
So sieht das dann aus:
[video=youtube;PYvDruVnO9g]https://www.youtube.com/watch?v=PYvDruVnO9g&feature=youtu.be[/video]

Das ist zugeschneiderte firmware. ASM interrupthandler für pwm, 8khz. Vollgas nur 3/4 weil der motor für das boot viel zu stark ist. Input auflösung ist 2048 schritte/ms, pwm auflösung ist 250 schritte. Man beachte den langsam-lauf. Mir gefällt das.
Fwd/rev ist auf einem schalter, gas nicht rückstellend auf knüppel, aus unten. Der mix ist in der funke gemacht.

Hier die timer2 asm interrupt handler. Einfach ins projekt kopieren und in avrstudio als source deklarieren. Dann die entsprechenden C interrupthandler auskommentieren. Die variable "fwddir" muss dann noch ins .h file verschoben werden. C und ASM mix ist eigentlich einfach.

Code:
#include <avr/io.h>
#include "bs_nfet_brushed.h"

.global TIMER2_OVF_vect

TIMER2_OVF_vect:
		sbi 0x12,ApFET
		sbi 0x15,CpFET
		reti


.global TIMER2_COMP_vect

TIMER2_COMP_vect:
        push  r16
		in r16, 0x3F ; save SREG
     	push r16		
		lds r16,fwddir

/*
		sbrs r16,0
		cbi	0x15,CpFET
		sbrc r16,0
		cbi 0x12,ApFET
     	pop r16       
     	out 0x3F, r16 ; restore SREG
        pop   r16
        reti
*/
		cpi	r16, 0x00
		brne notzero
		cbi	0x15,CpFET
     	pop r16       
     	out 0x3F, r16 ; restore SREG
        pop   r16
        reti
notzero:
		cbi 0x12,ApFET
     	pop r16       
     	out 0x3F, r16 ; restore SREG
        pop   r16
        reti
 
Zuletzt bearbeitet:
FPV1

Banggood

Oben Unten