Motor control using function generator

Hello, I'm trying to PID-control a DC motor using an arduino uno but I'm not aiming at a one-position command but more to a control which can follow a sine curve as a command (for the angular position of the motor or its speed). Hence I'm wondering if there is a possibility to use a function generator to give to the arduino the sine command signal for the control to follow. Any ideas ?

No need for a signal generator, just call the sin() function to set the control point
each time round the PID loop, or at some other regular rate.

[ Post your code if you want more than vague descriptions of what to do. ]

Isn't it possible that calling the sin function will take too much time at each loop and will delay the command I'm trying to follow ? It's because of that I'm asking. I haven't got the code with me but I will post it as soon as possible.

Just how fast are you running this PID loop?

Go here and grep for "sin()",40901.0.html

I need to run the PID loop with the highest frequency possible. I would like a sine command up to 5Hz. It seems I can’t run the loop faster than each 5 ms for what I’ve seen until now.

Here’s my code :

#include "Arduino.h"
#include "SimpleTimer.h"
#include "math.h"

int _DIRA = 13;
int _PWMA = 11;
int _BRKA = 8;

//Variables globales

long avancement;
long nbPoints;
long ampCmd;
long freq;

//Variables lecture encodeur

int encoderPin1 = 2;
int encoderPin2 = 3;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;

int lastMSB = 0;
int lastLSB = 0;

float Kp,Ki,Kd,in;

//Définition des erreurs pour le PID et des commandes

#define TRUNCATE(value, mi, ma) min(max(value, mi), ma)

// Maximum error integral
#define SERROR_MAX 32768

// Maximum PWM value
#define PWM_MAX 255

long i_error;
long lerreur;
long d_error;
long sp,dir,cmd,erreur,pos,time1,timecur,dt;

// Lecture de l'encodeur optique

void updateEncoder(){
  int MSB = digitalRead(encoderPin1); //most significant bit
  int LSB = digitalRead(encoderPin2); //least significant bit

  int encoded = (MSB << 2) |LSB; //concaténation des deux broches en un nombre
  int sum  = (lastEncoded << 1) | encoded; //valeur précédente + valeur actuelle
  //disjonction des cas pour déterminer le sens de rotation
  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++; //CW
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --; //CCW

  lastEncoded = encoded; 

// Calcul du PID pour déterminer la consigne

void test(int cmd)
  timecur=millis(); //Discrétisation temporelle en intervalles réguliers pour le calcul de l'erreur
  // Calcul erreur actuelle
  erreur = encoderValue - cmd;
  i_error = TRUNCATE(i_error + erreur*dt, -SERROR_MAX, SERROR_MAX);
  d_error = (erreur - lerreur)/dt;  

  // Mise en mémoire erreur
  lerreur = erreur;

  // Calcul de la commande
  sp = TRUNCATE(Kp*erreur + Ki*i_error + Kd*d_error,-255,255);
  // Choix de la direction
  if (sp>=0){
    dir = HIGH;
  } else {
    dir = LOW;
    sp = -sp;

// Envoi de la nouvelle consigne au moteur

void action()
  analogWrite(_PWMA, sp);
  digitalWrite(_DIRA, dir);

void setup()
  Serial.begin (9600);

  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);
  digitalWrite(encoderPin1, HIGH); 
  digitalWrite(encoderPin2, HIGH); 
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

void loop()
  static unsigned long dernier_top=0L;
  unsigned long nouveau_temps=millis();
  if (nouveau_temps - dernier_top >= 20)


Sorry for the commentaries, they’re in french and also forgive the bad optimisation of my code, it’s not my principal preoccupation…

You could try pre-computing a SIN lookup table and putting it in PROGMEM; if you don't mind a loss of accuracy, make the table 256 bytes long (that way you can use a byte to hold the "angle"). If you can take more of a hit in accuracy, you can also store the entries as integers of some sort - when you make the table, multiply the value for the SIN entry by some value (for however much accuracy you need) to make it an integer, then when you look it up, divide it by that same value to get the original value back (minus some accuracy because of dropped decimal places).

What is the encoder resolution and type? Is it Incremental or Absolute? (your code uses MSB and LSB, is it really absolute and if so how many bits? Personally if I were looking for speed I would avoid "floats" wherever possible. When deciding types, ask yourself "what is it and what is the min/max value". For example, your PWM command to the motor should be type "byte". If you have an encoder with 10 bit resolution, that should be type "unsigned int" and so on.

If you are concerned about speed or resolution, use a DUE. It has the RAW speed and also has 12 bit PWM resolution. It also has an encoder channel with hardware decoding but I'm not sure anyone has made a library for that feature yet. Of course it's a 3.3V device, so you will need level-shifters on your I/O but those are available and cheap.

I need to run the PID loop with the highest frequency possible.

What are the mechanical and electrical time constants of your motor?