Struggling generating values for non-linear PWM output

Status
Not open for further replies.

ilium007

Well-known member
Hi - I am building a model train controller and have most of it prototyped now but I'm having issues with the PWM output. I am testign with a Teensy++ 2.0 before moving to a Teensy 3.6 which I am yet to purchase. I am using a Pololu MC33926 Motor Driver Carrier which is working perfectly to drive trains around the track.

The issue is the amount the potentiometer (10k linear) has to be turned before the trains start moving. When I look at the 10-bit PWM value when they start it is about 750 (out of 1023). This corresponds with about 75% of the pot movement which is understandable.

I have got the code written to look up the current PWM value in a 1024 long array to then set the PWM duty cycle to a non-linear value but I am struggling with generating the 1024 non-linear values. I don't know how to get the list of numbers from either an S-curve, logarithmic, exponential function. I need 0 - 1023 input and a range of numbers output 0 - 1023 in a non-linear fashion if that makes sense.

I have been scouring the internet for this but but sure where to go next.
 
Maybe something like this?

Code:
  for (int i=0; i < 1023; i++) {
    pwmtable[i] = 38.945 * log2f((float)(i+8) * 0.125) + 750.49;
  }

Or if log base 2 isn't to your liking, substitute any other function you like and adjust constants so the 0-1023 range maps to the part of that function you like, and it's output maps to a range of 0-273, so when added with 750 you get the desired performance over the range that controls your motor.
 
Thanks Paul - I had just come up with this s-curve algorithm (from piecing together stuff from the net) and was about to test:

Screen Shot 2017-10-21 at 5.41.35 pm.jpg
 
However, one minor point to check... if you're using analogWrite() for the PWM, the number for controlling the PWM are 0 to 256, not 0 to 1023. Pretty easy to scale by just multiplying by 0.25.
 
I was using the following to write the value. I am running Timer1 at 50us to get 20kHz.

Code:
Timer1.setPwmDuty(speed0_pwm_pin, speed0_read);

Where speed0_read is 0-1023
 
I can't get the log2f function to work. I have included Math.h.

Code:
motor_control:60: error: 'log2f' was not declared in this scope
     pwmtable[i] = 38.945 * log2f((float)(i+8) * 0.125) + 750.49;
                                                      ^

I'm using the Teensy software v1.39 with the Teensy++ 2.0

I cloned the Teensy core libraries from GitHub and grep'd for log2f and found nothing.
 
Last edited:
I have spent 5 hours trying to get something to work (could not for the life of me get log2f to work). As a temporary measure I have resorted to using mulitMap() to generate something that resembles an S Curve. I eyeballed the numbers in Excel and transferred into the code. The train speed response is better but I want to understand this better and generate a proper 1024 value mapped table to set the speeds from.

I can't find anything online to help understand building an s-curve algorithm to fit existing data.

Code:
 //read the analog input
  speed0.speed0_read = constrain(analogRead(38),0,1023);

  // S curve - kind of....
  int s[]  = { 0,600,650,710,750,810,850,900,950,1000,1023};
  int in[] = { 0,20, 50,100,150,450,580,720,830,950,1023};
  
  speed0.read_mapped = multiMap(speed0.speed0_read, in, s, 11);
  
  if (speed0.last_print > 150) {
    Serial.println(speed0.speed0_read);
    Serial.println(speed0.read_mapped);
    speed0.last_print = 0;
  }
  
  Timer1.setPwmDuty(speed0_pwm_pin, speed0.read_mapped);
 
Oh, looks like the 8 bit AVR doesn't have log2f. It does work as-is on the 32 bit boards.

But there's a simple math trick, log2(x) = log(x) / log(2.0) you could use, where log is natural log.

Code:
for (int i=0; i < 1023; i++) {
      pwmtable[i] = 38.945 * (logf((float)(i+8) * 0.125)/logf(2.0)) + 750.49;
  }
 
I ended up using Desmos (https://www.desmos.com/calculator) to build an algorithm for a log base e curve that fit my requirements. I then created a temp sketch on the Teensy to generate the lookup table using this code (with thanks to Paul for giving me the structure) because on the Teensy++ 2.0 I need to put this table in PROGMEM as a const so I don't think I can build this on the fly during startup.

Its probably a bit of a hack but I'm still learning !

Code:
void setup() {
  Serial.begin(115200);

  int j = 0;
  for (int i=0; i < 1023; i++) {
    float val = 127.399 * log(3 * i);
    //Serial.print(i);
    //Serial.print(" - ");
    Serial.print(int(round(val)));
    Serial.print(",");
    if (j == 19) {
      Serial.print("\n");
      j = 0;
    }
    j++;
  }
}

Screen Shot 2017-10-22 at 3.59.36 pm.png
 
Status
Not open for further replies.
Back
Top