Author Topic: DC motor speed controller with regen braking, using ATTINY85/45.  (Read 11580 times)

0 Members and 1 Guest are viewing this topic.

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Decided to post my ongoing project here. It's not a big project but a fun one, i think.

I have this homemade e-scooter that has gone through various changes and upgrades.
Honestly it's a pile of junk (quite literally) but fun to play around with (especially for my younger brother).

I had made a simple PWM controller for the motor with an LM393, which worked pretty well. Until i popped it  :-BROKE
And before that i had just an on off switch that would either give power to the motor or short it out to brake.
I really liked the electric brake but there was no easy way to add it to the LM393 PWM controller so i decided to make a new one.

This one will be controlled by an Attiny85/45. I'm currently testing the code on a Digispark board and Aruino IDE because it's more convenient.
Once it's all done i'll flash the code to a spare Attiny45 using my USBAsp. But right now the code is still a bit of a mess.

Currently in my code i have:
*Throttle end point edjustment
*Brake end point adjustment (same pot as throttle)
*Dead band adjustment between throttle and brake.
*Throttle max PWM adjustment
*Brake max PWM adjustment
*Limp mode PWM adjustment (when battery is low)
*Battery voltage low treshold
*Battery voltage cutoff treshold
*Battery voltage sag compensation (because voltage sags under load)
*Battery voltage divider ratio (for the resistor divider)
*Runaway protection (for when you connect the battery)

Have yet to add to the code:
*Current measurement
*Current limit
*Hysteresis for state changes (cutoff <-> limp <-> full power)

Tell me if i missed some important feature.

The regen will be done quite crudely with a MOSFET in parallel to the freewheeling diode and a diode in parallel to the drive MOSFET.
Basically a boost converter in parallel to the driver.

I wanted to do the current measurement without a shunt by basically measuring the VDS-ON of the drive MOSFET but my PWM frequency is at 33kHz so my µcontroller would be too slow i think. 33kHz because i wanted no PWM whine.
So i think i'll just use a galvanized nail as a shunt (galvanized because they solder easier).
When stalled the motor draws about 60-ish amps, so i need a pretty good shunt, hence the nail.
And no i'm not buying a shunt, that would take too long and cost too much.

Code: [Select]
/* throttle and brake end point adjustment
works pretty much the same as on an RC car
  |<------------------------- ADC value from throttle potentiometer ---------------------->|
  |<------- throttle range --------->|<- dead band ->|<------ brake range for regen ------>|
  |                                  |               |                                     |
  |                                  |               |                                     |
  |                                  |               |                                     |
throttle_max_val           throttle_min_val       brake_min_val                      brake_max_val

*/

#define ADC_reference_voltage 2.56  // volts


/*~~~~ Start of user parameters ~~~~*/
// throttle values
  #define throttle_max_val    200 // expected throttle value max
  #define throttle_min_val    100 // expected throttle value min

//Brake values
  #define brake_min_val    80 // expected brake value min
  #define brake_max_val    50 // expected brake value max

// power limits
  uint8_t motor_PWM_max = 200;  // max power limit to motor from 0 to 255
  uint8_t motor_PWM_limp = 100; // max pwm when in limp mode once battery voltage is too low, cannot be above max PWM
  uint8_t brake_PWM_max = 255;  // max pwm to brake from 0 to 255
                        //note at 255 PWM brake there will be no regen
// battery parameters
  #define battery_voltage_low         13.6  // 3.4V per cell on a 4S pack   
  #define battery_voltage_cutoff      12.5  // 3.125V per cell
  #define battery_voltage_sag         10    // % of battery voltage sag at 100% throttle
  #define battery_voltage_divider_ratio   10  // battery voltage is divided for ADC by x value   
/*~~~~ end of user parameters ~~~~*/


// other stuff
#define throttle_range (throttle_max_val - throttle_min_val) // throttle range calculated
#define brake_range   (brake_min_val - brake_max_val) // min and max reversed fromthrottle because math
//                   ^^^    brackets are for math reasons


// variables
uint8_t throttle_val;   // throttle value variable for adc readout
uint16_t battery_val;    // battery value variable for adc readout
uint8_t PWM_to_throttle_register;  // calculated PWM value to send to register
uint8_t PWM_to_brake_register;     // calculated PWM value to send to register
uint16_t battery_voltage_ADC_range ; //
//uint8_t runaway_protection_flag; // self explanatory (and also not needed)

void setup() {

   
 
    GTCCR = 0b100000000; // timer setup register 1 = timer halted
    TCCR0B = 0b00000001;  // set clock source for timer
    TCCR0A = 0b10100001; // enable OC0A1 and set waveform gen to slow pwm
   
    DDRB = 0b00000011;  // set PB0 as output.

    OCR0A = 0;
    OCR0B = 0;

  ADMUX =
            (1 << ADLAR) |     // ADC works as 8-bit
            (1 << REFS1) |     // Set vref to 2.56V
            (1 << REFS2) |     // ^^^^^^^^^^^^^^^^^^
            (0 << MUX3)  |     //
            (0 << MUX2)  |     //
            (0 << MUX1)  |     //
            (1 << MUX0);       // ADC 1 on PB2

  ADCSRA =
            (1 << ADEN)  |     // Enable ADC
            (1 << ADPS2) |     // set prescaler to 64, bit 2
            (1 << ADPS1) |     // set prescaler to 64, bit 1
            (0 << ADPS0);      // set prescaler to 64, bit 0 


    // if throttle is not in the deadband wait until it is
    while (ADCH > throttle_min_val || ADCH < brake_min_val){
       ADCSRA |= (1 << ADSC);         // start ADC measurement
      while (ADCSRA & (1 << ADSC) ); // wait til conversion complete
    }
    // this should prevent runaway to some extent
   
}

void loop() {


  // measure throttle ADC
    // set analog multiplexer
  ADMUX &= ~( 1 << MUX1 ); // ADC 1 on PB2
 
    // measure voltage
  ADCSRA |= (1 << ADSC);         // start ADC measurement
    while (ADCSRA & (1 << ADSC) ); // wait til conversion complete
    throttle_val = ADCH;  // read adc val

//~~~~~~~~~~~~~~~~~~~~~~
  if (throttle_val > throttle_min_val){ // if throttle is applied
    PWM_to_brake_register = 0;  // brake PWM = 0
    if (throttle_val < throttle_max_val){
    PWM_to_throttle_register = ((uint16_t)(throttle_val - throttle_min_val)* motor_PWM_max) / throttle_range;  // scale value
    }
    else PWM_to_throttle_register = motor_PWM_max;
  }
  //~~~~~~~~~~~~~~~~~~~~~~~
   else if (throttle_val < brake_min_val){    // if brake is applied
    PWM_to_throttle_register = 0;  // throttle PWM = 0
    if (throttle_val > brake_max_val){
    PWM_to_brake_register = ((uint16_t)(brake_min_val - throttle_val) * brake_PWM_max) / brake_range ;  // scale value
    }
    else PWM_to_brake_register = brake_PWM_max;
  }
  else {
    PWM_to_brake_register = 0;
    PWM_to_throttle_register = 0;
  }

//*     // <- to comment out battery voltage measurement
  // measure battery voltage
    // set analog multiplexer
  ADMUX |= ( 1 << MUX1 ); // ADC 3 on PB3

    // measure the voltage
            ADCSRA |= (1 << ADSC);         // start ADC measurement
    while (ADCSRA & (1 << ADSC) ); // wait til conversion complete
    //battery_val = (ADCH << 2) | (ADCL >> 6);  // read adc val   
    battery_val = (uint16_t)(ADCL >> 6) | (uint16_t)(ADCH << 2);  // read adc val
   
    //adjust battery value according to load PWM
    battery_val += (((battery_val * battery_voltage_sag) / 100) * PWM_to_throttle_register ) / motor_PWM_max ;
    if (battery_val > 1023) battery_val = 1023;
//*/

  // transfer values to PWM registers
  OCR0A = PWM_to_brake_register; 
  //OCR0B = PWM_to_throttle_register;
   
//*
  // control throttle according to battery voltage
  if ( battery_val <= (uint32_t)(1023 * battery_voltage_cutoff) / (ADC_reference_voltage*battery_voltage_divider_ratio)){
    OCR0B = 0; // if battery too low no throttle
  }
  else {
    if ( battery_val <= (uint32_t)(1023 * battery_voltage_low) / (ADC_reference_voltage*battery_voltage_divider_ratio)){
    if (PWM_to_throttle_register >  motor_PWM_limp) OCR0B = motor_PWM_limp; // if battery too low no throttle
    }
    else OCR0B = PWM_to_throttle_register;
  }
 
//  */
}


Ps: now thinking about it i could totally build a driver with all these features without any microntrocontroller. Might be a project for another time.
« Last Edit: October 26, 2021, 06:18:55 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8170
  • Country: fi
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #1 on: October 26, 2021, 06:19:02 pm »
Regen comes automatically by just using half-bridge with two controllable switches, i.e., two MOSFETs instead of the simplest MOSFET+diode. As discussed in the other thread in the beginner section, diodes in parallel with MOSFETs are not usually needed.

A gate driver capable of driving that high-side MOSFET is needed, a bootstrap IC is the cheapest and simplest solution and quite OK at that power level.

Always keeping either of the MOSFETs on, i.e., switching between low and high and adjusting the duty cycle controls between drive / brake force. If you want to coast, you can carefully feed back the duty cycle so that current measures zero, or simply just disable both MOSFETs.

And yes, the circuit is inherently equivalent to a synchronous buck, which is also equivalent to synchronous boost! The motor is equivalent to the L and C of the buck/boost, L being the winding inductance and C the inertia, voltage over C being the back-EMF voltage (voltage generated by the motor, linearly dependent on the RPM).

When you do add the current sense (and I'd recommend: start here!), keep in mind it needs to be bidirectional. There are bidirectional current sense amplifiers which have a bias pin so that zero current equals some output voltage (such as Vcc/2).
« Last Edit: October 26, 2021, 06:22:24 pm by Siwastaja »
 
The following users thanked this post: Yansi

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8170
  • Country: fi
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #2 on: October 26, 2021, 06:24:00 pm »
... and use a ground side shunt (i.e., from lower FET source to ground) instead of Vds sensing because it's easier to implement. I like your idea about a nail. You can calibrate it in software.

An amplifier would be highly recommendable but maybe the nail can dissipate enough power to cause measurable voltage difference with ADC only. Try to come up with some kind of ghetto "Kelvin connection" though. Biasing for bidirectional sensing would require some trickery though.
« Last Edit: October 26, 2021, 06:26:08 pm by Siwastaja »
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #3 on: October 26, 2021, 09:53:00 pm »
Regen comes automatically by just using half-bridge with two controllable switches, i.e., two MOSFETs instead of the simplest MOSFET+diode. As discussed in the other thread in the beginner section, diodes in parallel with MOSFETs are not usually needed.

A gate driver capable of driving that high-side MOSFET is needed, a bootstrap IC is the cheapest and simplest solution and quite OK at that power level.

Always keeping either of the MOSFETs on, i.e., switching between low and high and adjusting the duty cycle controls between drive / brake force. If you want to coast, you can carefully feed back the duty cycle so that current measures zero, or simply just disable both MOSFETs.

And yes, the circuit is inherently equivalent to a synchronous buck, which is also equivalent to synchronous boost! The motor is equivalent to the L and C of the buck/boost, L being the winding inductance and C the inertia, voltage over C being the back-EMF voltage (voltage generated by the motor, linearly dependent on the RPM).

When you do add the current sense (and I'd recommend: start here!), keep in mind it needs to be bidirectional. There are bidirectional current sense amplifiers which have a bias pin so that zero current equals some output voltage (such as Vcc/2).

I considered the half bridge idea at first but was a bit afraid of accidentally turning on both MOSFETs at once, which would be pretty bad.
IIRC TCC1 in Attiny85/45 can generate two phase correct PWM signals with deadtime so i might look more into that.

As for the current measurement, i think i'll measure the low side current with a diff amp.
My Vref right now is the internal 2.56V, which i chose for stability.
But i might switch to the (potentially noisy) V+ as Vref. This way i can use V+/2 as a reference for a virtual ground for the diff amp.
Then at startup i would read the current at 0 PWM and assume that this value is my zero point.
Then i would only need to know the volt per amp ratio.
With a 5V reference my ADC would have approx 5mV resolution and about 4V of usable range, before i get into any non-linearities in the ADC and the diff amp.
And if i had, say 40mV/A, i could get about 0.125A resolution and a +-50A range.
I could use a simple 7805 to regulate my V+ and voltage stability wouldn't even matter because if V+ drifts so does the virtual ground along with it.

Looks like i might be whipping up a homemade PCB for this driver because this might be a little too complicated to build neatly on a perf-board.  :-/O
« Last Edit: October 26, 2021, 10:02:13 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline Benta

  • Super Contributor
  • ***
  • Posts: 5870
  • Country: de
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #4 on: October 26, 2021, 10:18:57 pm »
Somehow, the approach here seems to be backwards to me, but I'm probably just old fashioned.

If I were to do a design like this, I'd start with the load: OK, predifined, it's a bike. Then I'd move on to the motor and it's specifications.
This would in turn lead me on to the power stage needed for this motor. When this is specified, gate drivers defined, switching frequency for good regulation selected, power management and, and, and, THEN I'd look at which MCU might be suitable.

Starting with "...using ATTINY85/45" is totally foreign to me. Sorry.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #5 on: October 26, 2021, 10:59:08 pm »
Somehow, the approach here seems to be backwards to me, but I'm probably just old fashioned.

If I were to do a design like this, I'd start with the load: OK, predifined, it's a bike. Then I'd move on to the motor and it's specifications.
This would in turn lead me on to the power stage needed for this motor. When this is specified, gate drivers defined, switching frequency for good regulation selected, power management and, and, and, THEN I'd look at which MCU might be suitable.

Starting with "...using ATTINY85/45" is totally foreign to me. Sorry.

No need to apologise, this project is not the kind where you could calculate anything.
For starters, the DC motor is from the radiator cooling fan from my crashed SAAB - good luck finding the specs for that.
It's got enough power to push me at 17kph with a 4S lithium pack, which is pretty good considering what this motor was made for.
And even without a cooling fan the motor doesn't get too hot to touch.
Also it's not like i have a range of micros to choose from, rather i chose what i have in my parts bin.
The Attiny85/45 has enough PWM outs and analog ins to work, so why not?
I don't think it's worth going into deep effort for a side project.  :)


Edit: Found out why my previous LM393 PWM controller blew, turns out my freewheeling diode cooked and went open, which then took out my switching FET.  :-BROKE
 
« Last Edit: October 26, 2021, 11:20:22 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #6 on: October 27, 2021, 10:23:28 am »
Checked out the PWM with dead time in timer 1 and it seems pretty simple and straight forward.
But inverting one of the PWM outputs on timer 0 worked also.
Problem is that timer 0 might not be glitchless, i'll have to look into it some more.
I want to be able to control each output independently for coasting and pure regen when going downhill, for example.
I haven't yet found how to do that with timer 1.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #7 on: October 27, 2021, 12:32:50 pm »
Added some stuff to the code, there's now adjustable dead time for PWM when applying throttle and both FET's should switch like a synchronous buck.
When throttle is in the deadband the motor is coasting.
Because of the inverted brake PWM the brake is now applied at 100% at startup if throttle is not in the deadband because of the runaway protection.
I guess it's a feature now, right?  :-// No bugs here  ;D

Still a bunch of ideas tumbling around in my head.
The new throttle switching scheme makes braking kind of redundant, but i still want my motor to be able to free wheel also.
Problem is that (i think) now there will be a harsh transition between throttle and freewheeling while moving.


Updated code below.
Code: [Select]
/* throttle and brake end point adjustment
works pretty much the same as on an RC car
  |<------------------------- ADC value from throttle potentiometer ---------------------->|
  |<------- throttle range --------->|<- dead band ->|<------ brake range for regen ------>|
  |                                  |               |                                     |
  |                                  |               |                                     |
  |                                  |               |                                     |
throttle_max_val           throttle_min_val       brake_min_val                      brake_max_val

*/

#define ADC_reference_voltage 2.56  // volts


/*~~~~ Start of user parameters ~~~~*/
// throttle values
  #define throttle_max_val    200 // expected throttle value max
  #define throttle_min_val    100 // expected throttle value min

//Brake values
  #define brake_min_val    80 // expected brake value min
  #define brake_max_val    50 // expected brake value max

// power limits
  uint8_t motor_PWM_max = 220;  // max power limit to motor from 0 to 255
  uint8_t motor_PWM_limp = 100; // max pwm when in limp mode once battery voltage is too low, cannot be above max PWM
  uint8_t brake_PWM_max = 200;  // max pwm to brake from 0 to 255
  #define PWM_dead_time 10 // dead time for PWM
                        //note at 255 PWM brake there will be no regen
// battery parameters
  #define battery_voltage_low         1//13.6  // 3.4V per cell on a 4S pack   
  #define battery_voltage_cutoff      1//12.5  // 3.125V per cell
  #define battery_voltage_sag         10    // % of battery voltage sag at 100% throttle
  #define battery_voltage_divider_ratio   10  // battery voltage is divided for ADC by x value   
/*~~~~ end of user parameters ~~~~*/


// other stuff
#define throttle_range (throttle_max_val - throttle_min_val) // throttle range calculated
#define brake_range   (brake_min_val - brake_max_val) // min and max reversed fromthrottle because math
//                   ^^^    brackets are for math reasons


// variables
uint8_t throttle_val;   // throttle value variable for adc readout
uint16_t battery_val;    // battery value variable for adc readout
uint8_t PWM_to_throttle_register;  // calculated PWM value to send to register
uint8_t PWM_to_brake_register;     // calculated PWM value to send to register
uint16_t battery_voltage_ADC_range ; //
//uint8_t runaway_protection_flag; // self explanatory (and also not needed)

void setup() {

   
 
    GTCCR = 0b100000000; // timer setup register 1 = timer halted
    TCCR0B = 0b00000001;  // set clock source for timer
    TCCR0A = 0b11100001; // enable and set waveform gen to slow pwm
    // A output inverted and B output not inverted.
   
    DDRB = 0b00000011;  // set PB0 and PB1 as output.

    OCR0A = 0;
    OCR0B = 0;

  ADMUX =
            (1 << ADLAR) |     // ADC works as 8-bit
            (1 << REFS1) |     // Set vref to 2.56V
            (1 << REFS2) |     // ^^^^^^^^^^^^^^^^^^
            (0 << MUX3)  |     //
            (0 << MUX2)  |     //
            (0 << MUX1)  |     //
            (1 << MUX0);       // ADC 1 on PB2

  ADCSRA =
            (1 << ADEN)  |     // Enable ADC
            (1 << ADPS2) |     // set prescaler to 64, bit 2
            (1 << ADPS1) |     // set prescaler to 64, bit 1
            (0 << ADPS0);      // set prescaler to 64, bit 0 


    // if throttle is not in the deadband wait until it is
    while (ADCH > throttle_min_val || ADCH < brake_min_val){
       ADCSRA |= (1 << ADSC);         // start ADC measurement
      while (ADCSRA & (1 << ADSC) ); // wait til conversion complete
    }
    // this should prevent runaway to some extent
   
}

void loop() {


  // measure throttle ADC
    // set analog multiplexer
  ADMUX &= ~( 1 << MUX1 ); // ADC 1 on PB2
 
    // measure voltage
  ADCSRA |= (1 << ADSC);         // start ADC measurement
    while (ADCSRA & (1 << ADSC) ); // wait til conversion complete
    throttle_val = ADCH;  // read adc val

//~~~~~~~~~~~~~~~~~~~~~~
  if (throttle_val > throttle_min_val){ // if throttle is applied
    if (throttle_val < throttle_max_val){
      PWM_to_throttle_register = ((uint16_t)(throttle_val - throttle_min_val)* motor_PWM_max) / throttle_range;  // scale value
    }
    else {
      PWM_to_throttle_register = motor_PWM_max;
    }

  if (255 - PWM_to_throttle_register > PWM_dead_time){
    PWM_to_brake_register = (~PWM_to_throttle_register) - PWM_dead_time;
    if (PWM_to_brake_register > brake_PWM_max) PWM_to_brake_register = brake_PWM_max;
  }
  else PWM_to_brake_register = 0;

  }
  //~~~~~~~~~~~~~~~~~~~~~~~
   else if (throttle_val < brake_min_val){    // if brake is applied
    PWM_to_throttle_register = 0;  // throttle PWM = 0
    if (throttle_val > brake_max_val){
    PWM_to_brake_register = ((uint16_t)(brake_min_val - throttle_val) * brake_PWM_max) / brake_range ;  // scale value
    }
    else PWM_to_brake_register = brake_PWM_max;
  }
  else {
    PWM_to_brake_register = 0;
    PWM_to_throttle_register = 0;
  }

//*  <- to comment out battery voltage measurement
  // measure battery voltage
    // set analog multiplexer
  ADMUX |= ( 1 << MUX1 ); // ADC 3 on PB3

    // measure the voltage
            ADCSRA |= (1 << ADSC);         // start ADC measurement
    while (ADCSRA & (1 << ADSC) ); // wait til conversion complete
    //battery_val = (ADCH << 2) | (ADCL >> 6);  // read adc val   
    battery_val = (uint16_t)(ADCL >> 6) | (uint16_t)(ADCH << 2);  // read adc val
   
    //adjust battery value according to load PWM
    battery_val += (((battery_val * battery_voltage_sag) / 100) * PWM_to_throttle_register ) / motor_PWM_max ;
    if (battery_val > 1023) battery_val = 1023;
//*/

  // transfer values to PWM registers
  OCR0A = ~PWM_to_brake_register; 
  //OCR0B = PWM_to_throttle_register;
   
//*
  // control throttle according to battery voltage
  if ( battery_val <= (uint32_t)(1023 * battery_voltage_cutoff) / (ADC_reference_voltage*battery_voltage_divider_ratio)){
    OCR0B = 0; // if battery too low no throttle
  }
  else {
    if ( battery_val <= (uint32_t)(1023 * battery_voltage_low) / (ADC_reference_voltage*battery_voltage_divider_ratio)){
    if (PWM_to_throttle_register >  motor_PWM_limp) OCR0B = motor_PWM_limp; // if battery too low no throttle
    }
    else OCR0B = PWM_to_throttle_register;
  }
 
//  */
}

I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8170
  • Country: fi
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #8 on: October 27, 2021, 12:42:07 pm »
I considered the half bridge idea at first but was a bit afraid of accidentally turning on both MOSFETs at once, which would be pretty bad.

This is non-issue if you use a half-bridge gate driver, which is the easiest and simplest and lowest-cost solution anyway. You'd struggle to find one which allows you to do overlapped driving! Almost all implement deadtimes, some even have just "IN" and "EN" inputs which is easier to drive from a single PWM pin and such accident is impossible to happen. Others have separate IN_H and IN_L, but still implement protection against both being accidentally '1', so that valid combinations are 00, 01, and 10.

Quote
IIRC TCC1 in Attiny85/45 can generate two phase correct PWM signals with deadtime so i might look more into that.

IIRC it indeed can, but even if it couldn't, you could just use a half bridge driver with IN/EN inputs. IN driven by single PWM channel, EN with a GPIO.

Quote
As for the current measurement, i think i'll measure the low side current with a diff amp.

It's the best way because choosing a suitable amp allows you to bias the output for bidirectional sensing.

Although if you want to cheap-ass it, IIRC Tiny25/55/85 comes with a differential x20 gain ADC mode (unless I'm mixing it with something else in my head) and this could work with common mode of a few hundred mV below ground; being able to directly measure the shunt voltage. Worth testing if you want to reduce number of parts.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #9 on: October 27, 2021, 05:49:50 pm »
I considered the half bridge idea at first but was a bit afraid of accidentally turning on both MOSFETs at once, which would be pretty bad.

This is non-issue if you use a half-bridge gate driver, which is the easiest and simplest and lowest-cost solution anyway. You'd struggle to find one which allows you to do overlapped driving! Almost all implement deadtimes, some even have just "IN" and "EN" inputs which is easier to drive from a single PWM pin and such accident is impossible to happen. Others have separate IN_H and IN_L, but still implement protection against both being accidentally '1', so that valid combinations are 00, 01, and 10.

Quote
IIRC TCC1 in Attiny85/45 can generate two phase correct PWM signals with deadtime so i might look more into that.

IIRC it indeed can, but even if it couldn't, you could just use a half bridge driver with IN/EN inputs. IN driven by single PWM channel, EN with a GPIO.

Quote
As for the current measurement, i think i'll measure the low side current with a diff amp.

It's the best way because choosing a suitable amp allows you to bias the output for bidirectional sensing.

Although if you want to cheap-ass it, IIRC Tiny25/55/85 comes with a differential x20 gain ADC mode (unless I'm mixing it with something else in my head) and this could work with common mode of a few hundred mV below ground; being able to directly measure the shunt voltage. Worth testing if you want to reduce number of parts.
I don't have any gate drivers on hand and i don't feel like ordering them and waiting for them to arrive.
I think a simple gate driver with a couple NPN transistors should work well enough. At least my LTspice sim appears to say so.
The Attiny has diff amps with adjustable gain but that would take two IO pins instead of just one.
So an external diff amp is my only choice.
I don't want to use the RESET pin as an IO because that would require a high voltage programmer, which i don't have.
So in total i have 5 IO pins that i can use and right now all of them are taken up.
PB0 - low side FET PWM
PB1 - high side FET PWM
PB2 - throttle signal
PB3 - battery voltage measurement
PB4 - (soon to be) current measurement

It is possible to use PB5 (reset) as an IO without needing a high voltage programmer. For example using it with an ADC and staying below the reset threshold.
So in a pinch i can use it, but for now i think everything fits well enough.

Included is a pic of my high side driver on LTspice. Not with the exact components but as a quick test works well enough and can be refined later.  :-/O
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online magic

  • Super Contributor
  • ***
  • Posts: 6774
  • Country: pl
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #10 on: October 27, 2021, 06:16:30 pm »
I think you could get a half-bridge guaranteed to be crossconduction-free if you use complementary source followers for the switches.
This needs low power boosted rails on both the negative and positive side, however.

I see no reason to use Schottky for D1 in your circuit. Forward voltage is inconsequential but leakage is bad.
« Last Edit: October 27, 2021, 06:20:26 pm by magic »
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8170
  • Country: fi
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #11 on: October 27, 2021, 06:40:22 pm »
In any case, I don't find the cross conduction to be a huge risk item if you write the code to configure the timer with dead time generation, then verify with scope before soldering in the MOSFETs. That part of code is unlikely to suddenly break later.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #12 on: October 27, 2021, 09:31:41 pm »
In any case, I don't find the cross conduction to be a huge risk item if you write the code to configure the timer with dead time generation, then verify with scope before soldering in the MOSFETs. That part of code is unlikely to suddenly break later.
I've already implemented dead time generation with a parameter to set it in the code above.
But i don't like that when transitioning from the dead band to throttle i'll be transitioning from 0% to brake_PWM_max in one step.
Perhaps i should keep regen to a minimum when in the throttle range? Just enough to freewheel.
But then the more load on the motor the more freewheeling there will be and i can't let that go through the body diode of the FET.
I think i should build the thing so that i can test it out. After i figure out the current sensing fully, of course.

I think you could get a half-bridge guaranteed to be crossconduction-free if you use complementary source followers for the switches.
This needs low power boosted rails on both the negative and positive side, however.

I see no reason to use Schottky for D1 in your circuit. Forward voltage is inconsequential but leakage is bad.
It's just my habit to put Schottkeys everywhere when playing with LTspice. I assume a 1N4148 would work for this also.

I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline Benta

  • Super Contributor
  • ***
  • Posts: 5870
  • Country: de
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #13 on: October 27, 2021, 10:12:50 pm »
For starters, the DC motor is from the radiator cooling fan from my crashed SAAB - good luck finding the specs for that.
It's got enough power to push me at 17kph with a 4S lithium pack, which is pretty good considering what this motor was made for.

So it's like a "Zeppelin Bike"? Cool.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #14 on: October 27, 2021, 10:43:14 pm »
For starters, the DC motor is from the radiator cooling fan from my crashed SAAB - good luck finding the specs for that.
It's got enough power to push me at 17kph with a 4S lithium pack, which is pretty good considering what this motor was made for.

So it's like a "Zeppelin Bike"? Cool.
Not exactly what you would call a zeppelin, i'll include a pic.
The fan was smashed so i just extracted the DC motor and strapped it to my old scooter that i got way back when i was about 5 years old.
So this hot pink (BigClive approved, no doubt) scooter has been through hell and back and is likely on it's last journey before the inevitable scrap pile.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8170
  • Country: fi
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #15 on: October 28, 2021, 08:45:28 am »
Well for the simplest first iteration, just make the throttle command the duty cycle and use a half bridge. That way, the transition between drive / brake is completely smooth. The exact duty cycle number where the transition happens depends on the RPM, though.

The next step would be to add current regulation loop, a simple PI loop works well and P, I tuning is noncritical as long as it's on the right ballpark. Input to the PI loop is the current measurement, output is duty cycle.

This way you can make the throttle lever just set current setpoint which would a signed number, again completely smooth transition between drive / brake.

Whichever you do, smooth the lever input for example by moving cumulative average so that you don't have sudden changes in setpoint.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8170
  • Country: fi
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #16 on: October 28, 2021, 08:49:36 am »
Starting with "...using ATTINY85/45" is totally foreign to me. Sorry.

I understand that kind of approach and sometimes do it myself. For hobby projects, it's kind of obvious, but it's a valuable skill for professional life as well, now it's evidenced by the component crisis and needing to be able to work with whatever parts you can get.

ATTiny45/85 specifically is a good example because IMHO, it's kind of the smallest microcontroller with the minimum amount of peripherals that can successfully implement a brushed DC motor controller. It has ADC, analog comparator and dual-channel PWM with deadtime generation, and enough CPU oomph and memory to run a few PI(D) loops and a simple UI. So, while quite small for the job, it's not an irrational choice either.

If it was a BLDC controller, I would suggest some STM32F030 part as the minimum although there are parts in AVR series which can do that, but they are more expensive than the smallest ARM parts.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #17 on: October 28, 2021, 02:48:50 pm »
Quickly drew up a schematic. Might be the final version, might not.
I have some IRFB4110 and IRF1324's. The IRF540's in the schematic are only placeholders.
Same pinout, same TO-220 so who cares, right.
To drive the gates i chose TO-92 BC547's because i have a bag of those.
But i might swap them for some SMD 2N3904's just because drilling holes is tedious.
R5 and R6 are very important, because a nasty side efect of this gate driver is that it's on by default.
So if my micro pins are ever high Z the pullup resistors will keep the gates low.
Might need to add pads for a programming header for the ATtiny also.
Or just might make a clip to put on the IC instead, would be more versatile in the long run as i could use it on other DIP-8 IC's.
Also i think one FET per side should be enough, at least it was enough for my previous driver.
Since op-amps come in pairs most of the time i used the spare one to buffer the diff amp reference.
And added a potentiometer instead of a set voltage divider in case my op-amp has significant offset, which would get amplified by the gain.

Feel free to criticize.

Edit: oops forgot to add the voltage divider for the battery voltage measurement.  :palm: fixed
« Last Edit: October 28, 2021, 02:59:15 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #18 on: October 28, 2021, 06:49:43 pm »
Oh and it looks like both of my gate drive signals need to be inverted because of the drivers.
No biggie, just two bits in TCCR0A.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #19 on: October 29, 2021, 06:00:13 pm »
Turns out i had forgotten the pin header for the throttle potentiometer also, so i just bodged a trimmer in, same pitch anyways.
Drew up a schematic, leaving some space to bend the MOSFETs over.
Will probably mount THT parts on the surface because i don't feel like drilling holes  ;D
Didn't bother cleaning the silkscreen up because it's a homebrew board anyways.
Had some 50x70mm single sided FR4 substrate so that's how big i made the board.
And will have to add two jumper wires because i couldn't make two last traces connect, not great, not terrible.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #20 on: October 31, 2021, 03:28:31 pm »
In my infinite wisdom i managed to etch the board mirrored.  |O
So component pins are mirrored also. No big deal for the transistors and FETs but pretty terrible for the ICs.
To work around this i basically just bent the pins on the IC's over and soldered them upside down.
Managed to find my USBAsp and tried programming my Attiny45 with it but got errors.
A quick glance at the schematic reveals that i have capacitors on data pins - oops.
Before making any changes to the board i pulled out another Attiny45 and tried programming it on the breadboard - no dice.
I keep running into this error:
Code: [Select]
avrdude: warning: cannot set sck period. please check for usbasp firmware update.
Pretty weird, because the Attiny's i'm trying to flash have already been flashed with this exact USBAsp many moons ago.

Anyways, looks like i'll be butchering a Digispark board.
Bummer.
« Last Edit: October 31, 2021, 03:31:31 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #21 on: November 01, 2021, 12:33:20 pm »
Pulled the Attiny85 off my digispark board but it doesn't seem to like booting with all the auxillary stuff connected to it.
I assume it's a problem with the bootloader.
Regardless, i got the thing running.
Next problem i noticed is the slow gate rise/fall times (~800 ns), which seems to have come as a result of the 47 Ohm gate resistors.
I chose these resistors to keep the peak current down so that my BC547's don't pop.
Playing around with a small motor on the desk it seems to work allright.
But another problem i noticed is that my JRC4558 is acting weird.
Adjusting the offset voltage down close to 0 the buffer amp shoots up to V+. Yet it works normally above 1-ish V.
And also my diff amp seems to not work at all.
I'll have to go to LTspie to check my sanity, because all connections seem correct.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online magic

  • Super Contributor
  • ***
  • Posts: 6774
  • Country: pl
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #22 on: November 01, 2021, 12:51:42 pm »
455x-456x stuff has phase reversal at the negative rail just like TL072 (and they are indeed almost identical).
Replace with LM358 or add a negative supply.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1542
  • Country: lt
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #23 on: November 01, 2021, 01:42:39 pm »
455x-456x stuff has phase reversal at the negative rail just like TL072 (and they are indeed almost identical).
Replace with LM358 or add a negative supply.
Replaced with a HA17358 and that fixed the problem.
I had also mixed up a 20k and a 1k resistor so after swapping those around i finally get my current output.
The signal looked noisy so i added an RC filter (6k8+10nF - arbitrary values), which seems to clean up the switching noise pretty well.
The signal is still wavy because of the motor rotating and doing it's thing, but that's ok (i think).
Current measurement both ways seems to work as intended.
What you can see in the second screenshot is me turning the throttle potentiometer back and forth so when the motor decelerates it does a little bit of regen.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8170
  • Country: fi
Re: DC motor speed controller with regen braking, using ATTINY85/45.
« Reply #24 on: November 01, 2021, 02:45:41 pm »
Can you synchronize ADC conversion to the same timer you use to generate PWM in ATTiny85? I don't remember. If you can, you can avoid switching noise by sampling at a convenient point.

You can add some weight on the motor axle to get some momentum if you want to bench test the regen a bit better, although that trace already shows it seems to work.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf