dc_motor.c

Go to the documentation of this file.
00001 
00047 #include "dc_motor.h"
00048 #include "hw/hw_dc_motor.h"
00049 
00050 // Define logging setting (for cfg/log.h module).
00051 #define LOG_LEVEL         DC_MOTOR_LOG_LEVEL
00052 #define LOG_FORMAT        DC_MOTOR_LOG_FORMAT
00053 
00054 #include <cfg/log.h>
00055 #include <cfg/debug.h>
00056 
00057 #include <algo/pid_control.h>
00058 
00059 #include <drv/timer.h>
00060 
00061 #include <kern/proc.h>
00062 
00063 #include <cpu/power.h>
00064 
00065 #include <string.h>
00066 
00070 #define DC_MOTOR_ACTIVE           BV(0)     ///< DC motor enable or disable flag.
00071 #define DC_MOTOR_DIR              BV(1)     ///< Spin direction of DC motor.
00072 
00073 /*
00074  * Some utility macro for motor directions
00075  */
00076 #define POS_DIR                   1
00077 #define NEG_DIR                   0
00078 #define DC_MOTOR_POS_DIR(x)       ((x) |= DC_MOTOR_DIR)   // Set directions status positive
00079 #define DC_MOTOR_NEG_DIR(x)       ((x) &= ~DC_MOTOR_DIR)  // Set directions status negative
00080 
00081 // Update the status with current direction
00082 #define DC_MOTOR_SET_STATUS_DIR(status, dir) \
00083         (dir == POS_DIR ? DC_MOTOR_POS_DIR(status) : DC_MOTOR_NEG_DIR(status))
00084 
00085 #if (CONFIG_KERN && CONFIG_KERN_PREEMPT)
00086     #if CONFIG_DC_MOTOR_USE_SEM
00087         #include <kern/sem.h>
00088 
00089         Semaphore dc_motor_sem;
00090         #define DC_MOTOR_LOCK        sem_obtain(&dc_motor_sem)
00091         #define DC_MOTOR_UNLOCK      sem_release(&dc_motor_sem)
00092     #else
00093         #define DC_MOTOR_LOCK        proc_forbid()
00094         #define DC_MOTOR_UNLOCK      proc_permit()
00095     #endif
00096 #else
00097     #define DC_MOTOR_LOCK        /* None */
00098     #define DC_MOTOR_UNLOCK      /* None */
00099 #endif
00100 
00104 static DCMotor dcm_all[CONFIG_NUM_DC_MOTOR];
00105 
00106 /*
00107  * Process to poll dc motor status
00108  */
00109 struct Process *dc_motor;
00110 
00111 
00112 // Stack process for DC motor poll.
00113 static PROC_DEFINE_STACK(dc_motor_poll_stack, 500);
00114 
00115 // Only for Debug
00116 LOG_INFOB(static int debug_msg_delay = 0;);
00117 
00118 
00119 INLINE dc_speed_t dc_motor_readSpeed(int index)
00120 {
00121     DCMotor *dcm = &dcm_all[index];
00122     return HW_DC_MOTOR_READ_VALUE(dcm->cfg->adc_ch, dcm->cfg->adc_min, dcm->cfg->adc_max);
00123 }
00124 
00128 dc_speed_t dc_motor_readTargetSpeed(int index)
00129 {
00130     DCMotor *dcm = &dcm_all[index];
00131     return HW_DC_MOTOR_READ_VALUE(dcm->cfg->speed_dev_id, CONFIG_DC_MOTOR_MIN_SPEED, CONFIG_DC_MOTOR_MAX_SPEED);
00132 }
00133 
00134 static void dc_motor_start(int index)
00135 {
00136     DCMotor *dcm = &dcm_all[index];
00137 
00138     DC_MOTOR_LOCK;
00139     /*
00140      * Clean all PID stutus variable, becouse
00141      * we start with new one.
00142      */
00143     pid_control_reset(&dcm->pid_ctx);
00144     dcm->status |= DC_MOTOR_ACTIVE;
00145     DC_MOTOR_UNLOCK;
00146 }
00147 
00148 /*
00149  * There are two \a mode to stop the dc motor:
00150  *  - DC_MOTOR_DISABLE_MODE
00151  *  - DC_MOTOR_IDLE
00152  *
00153  * The DC_MOTOR_DISABLE_MODE shut down the DC motor and
00154  * leave it floating to rotate.
00155  * The DC_MOTOR_IDLE does not shut down DC motor, but put
00156  * its supply pin in short circuite, in this way the motor result
00157  * braked from intentional rotation.
00158  */
00159 static void dc_motor_stop(int index)
00160 {
00161     DCMotor *dcm = &dcm_all[index];
00162 
00163     DC_MOTOR_LOCK;
00164 
00165     dcm->status &= ~DC_MOTOR_ACTIVE;
00166     dcm->expire_time = DC_MOTOR_NO_EXPIRE;
00167     pwm_enable(dcm->cfg->pwm_dev, false);
00168 
00169     if (dcm->cfg->braked)
00170     {
00171         DC_MOTOR_STOP_BRAKED(dcm->index);
00172     }
00173     else
00174     {
00175         DC_MOTOR_STOP_FLOAT(dcm->index);
00176     }
00177 
00178     DC_MOTOR_UNLOCK;
00179 }
00180 
00181 /*
00182  * Sampling a signal on DC motor and compute
00183  * a new value of speed according with PID control.
00184  */
00185 static void dc_motor_do(int index)
00186 {
00187     DCMotor *dcm = &dcm_all[index];
00188 
00189     dc_speed_t curr_pos = 0;
00190     pwm_duty_t new_pid = 0;
00191 
00192     DC_MOTOR_LOCK;
00193 
00194     //If select DC motor is not active we return
00195     if (!(dcm->status & DC_MOTOR_ACTIVE))
00196     {
00197         DC_MOTOR_UNLOCK;
00198         return;
00199     }
00200 
00201     /*
00202      * To set dc motor direction we must also set the
00203      * PWM polarity according with dc motor driver chip
00204      */
00205     pwm_setPolarity(dcm->cfg->pwm_dev, dcm->status & DC_MOTOR_DIR);
00206     DC_MOTOR_SET_DIR(dcm->index, dcm->status & DC_MOTOR_DIR);
00207 
00208     //Compute next value for reaching target speed from current position
00209     if (dcm->cfg->pid_enable)
00210     {
00211         /*
00212          * Here we cannot disable the switch context because the
00213          * driver, that read the speed could be need to use signal or
00214          * other thing that needs the kernel switch context, for this
00215          * reason we unlock before to read the speed.
00216          */
00217         DC_MOTOR_UNLOCK;
00218         curr_pos = dc_motor_readSpeed(index);
00219         DC_MOTOR_LOCK;
00220         new_pid = pid_control_update(&dcm->pid_ctx, dcm->tgt_speed, curr_pos);
00221     }
00222     else
00223     {
00224         new_pid = dcm->tgt_speed;
00225     }
00226 
00227     LOG_INFOB(
00228         if (debug_msg_delay == 20)
00229         {
00230             LOG_INFO("DC Motor[%d]: curr_speed[%d],curr_pos[%d],tgt[%d]\n", dcm->index,
00231                                 curr_pos, new_pid, dcm->tgt_speed);
00232             debug_msg_delay = 0;
00233         }
00234         debug_msg_delay++;
00235     );
00236 
00237     //Apply the compute duty value
00238     pwm_setDuty(dcm->cfg->pwm_dev, new_pid);
00239 
00240     //Restart dc motor
00241     pwm_enable(dcm->cfg->pwm_dev, true);
00242 
00243     DC_MOTOR_ENABLE(dcm->index);
00244     DC_MOTOR_UNLOCK;
00245 }
00246 
00247 
00248 /*
00249  * Check if the DC motor run time is expired, if this happend
00250  * we turn off motor and reset status.
00251  */
00252 INLINE bool check_timerIsExpired(int index)
00253 {
00254 
00255     DC_MOTOR_LOCK;
00256     bool check = ((dcm_all[index].expire_time - timer_clock()) < 0) &&
00257             (dcm_all[index].expire_time != DC_MOTOR_NO_EXPIRE);
00258     DC_MOTOR_UNLOCK;
00259 
00260     return check;
00261 }
00262 
00276 static void NORETURN dc_motor_poll(void)
00277 {
00278     for (;;)
00279     {
00280         /*
00281          * For all DC motor we read and process output singal,
00282          * and choose the max value to off time
00283          */
00284         for (int i = 0; i < CONFIG_NUM_DC_MOTOR; i++)
00285         {
00286             if (!dcm_all[i].cfg)
00287                 continue;
00288 
00289             if (check_timerIsExpired(i))
00290                 dc_motor_stop(i);
00291             else
00292                 dc_motor_do(i);
00293 
00294             /*
00295              * If we read speed from trimmer we update the target
00296              * speed value when motor is running so we can make
00297              * dc motor speed regulation.
00298              */
00299             if (dcm_all[i].cfg->speed_dev_id != DC_MOTOR_NO_DEV_SPEED)
00300                 dc_motor_setSpeed(i, dc_motor_readTargetSpeed(i));
00301         }
00302 
00303         //Wait for next sampling
00304         timer_delay(CONFIG_DC_MOTOR_SAMPLE_PERIOD - CONFIG_DC_MOTOR_SAMPLE_DELAY);
00305 
00306         for (int i = 0; i < CONFIG_NUM_DC_MOTOR; i++)
00307         {
00308             if (!dcm_all[i].cfg)
00309                 continue;
00310 
00311             if (check_timerIsExpired(i))
00312                 dc_motor_stop(i);
00313 
00314             DC_MOTOR_LOCK;
00315             if (dcm_all[i].status & DC_MOTOR_ACTIVE)
00316             {
00317                 DC_MOTOR_DISABLE(dcm_all[i].index);
00318                 pwm_enable(dcm_all[i].cfg->pwm_dev, false);
00319             }
00320             DC_MOTOR_UNLOCK;
00321         }
00322 
00323         //Wait some time to allow signal to stabilize before sampling
00324         timer_delay(CONFIG_DC_MOTOR_SAMPLE_DELAY);
00325     }
00326 }
00327 
00334 void dc_motor_setDir(int index, bool dir)
00335 {
00336     DCMotor *dcm = &dcm_all[index];
00337     DC_MOTOR_LOCK;
00338     DC_MOTOR_SET_STATUS_DIR(dcm->status, dir);
00339     DC_MOTOR_UNLOCK;
00340 }
00341 
00345 void dc_motor_setSpeed(int index, dc_speed_t speed)
00346 {
00347     DCMotor *dcm = &dcm_all[index];
00348 
00349     DC_MOTOR_LOCK;
00350     dcm->tgt_speed = speed;
00351     DC_MOTOR_UNLOCK;
00352 
00353     LOG_INFO("DC Motor[%d]: tgt_speed[%d]\n", index, dcm->tgt_speed);
00354 }
00355 
00359 void dc_motor_startTimer(int index, mtime_t on_time)
00360 {
00361     DC_MOTOR_LOCK;
00362     dcm_all[index].expire_time = DC_MOTOR_NO_EXPIRE;
00363     if (on_time != DC_MOTOR_NO_EXPIRE)
00364     {
00365         dcm_all[index].expire_time = timer_clock() + ms_to_ticks(on_time);
00366         dc_motor_start(index);
00367     }
00368     DC_MOTOR_UNLOCK;
00369 }
00370 
00371 void dc_motor_waitStop(int index)
00372 {
00373     DCMotor *dcm = &dcm_all[index];
00374     bool loop = true;
00375 
00376     while (loop)
00377     {
00378         DC_MOTOR_LOCK;
00379         loop = dcm->status & DC_MOTOR_ACTIVE;
00380         DC_MOTOR_UNLOCK;
00381 
00382         cpu_relax();
00383     }
00384 }
00385 
00389 void dc_motor_enable(int index, bool state)
00390 {
00391     if (state)
00392         dc_motor_start(index);
00393     else
00394         dc_motor_stop(index);
00395 }
00396 
00400 void dc_motor_setup(int index, DCMotorConfig *dcm_conf)
00401 {
00402     DCMotor *dcm = &dcm_all[index];
00403 
00404     DC_MOTOR_LOCK;
00405     /*
00406      * We are using the same sample period for each
00407      * motor, and so we check if this value is the same
00408      * for all. The sample period time is defined in pid
00409      * configuration.
00410      *
00411      * TODO: Use a different sample period for each motor
00412      * and refactor a module to allow to use a timer interrupt,
00413      * in this way we can controll a DC motor also without a
00414      * kernel, increasing a portability on other target.
00415      */
00416     pid_control_setPeriod(&dcm_conf->pid_cfg, CONFIG_DC_MOTOR_SAMPLE_PERIOD);
00417 
00418     //Init pid control
00419     pid_control_init(&dcm->pid_ctx, &dcm_conf->pid_cfg);
00420 
00421 
00422     dcm->cfg = dcm_conf;
00423 
00424     /*
00425      * Apply config value.
00426      */
00427     dcm->index = index;
00428 
00429     /*
00430      * By default the motor run forever..
00431      */
00432     dcm->expire_time = DC_MOTOR_NO_EXPIRE;
00433 
00434     /*
00435      * By default set target speed.
00436      */
00437     dcm->tgt_speed = dcm_conf->speed;
00438 
00439     /*
00440      * Clear the status.
00441      */
00442     dcm->status = 0;
00443 
00444     pwm_setFrequency(dcm->cfg->pwm_dev, dcm->cfg->freq);
00445     pwm_enable(dcm->cfg->pwm_dev, false);
00446 
00447     //Set default direction for DC motor
00448     DC_MOTOR_SET_DIR(dcm->index, dcm->cfg->dir);
00449     DC_MOTOR_SET_STATUS_DIR(dcm->status, dcm->cfg->dir);
00450 
00451     DC_MOTOR_UNLOCK;
00452 
00453     LOG_INFO("DC motor[%d]:\n", dcm->index);
00454     LOG_INFO("> PID: kp[%f],ki[%f],kd[%f]\n", dcm->cfg->pid_cfg.kp, dcm->cfg->pid_cfg.ki, dcm->cfg->pid_cfg.kd);
00455     LOG_INFO("> PWM: pwm_dev[%d], freq[%ld], sample[%d]\n", dcm->cfg->pwm_dev, dcm->cfg->freq,CONFIG_DC_MOTOR_SAMPLE_DELAY);
00456     LOG_INFO("> ADC: adc_ch[%d], adc_max[%d], adc_min[%d]\n", dcm->cfg->adc_ch, dcm->cfg->adc_max, dcm->cfg->adc_min);
00457     LOG_INFO("> DC: dir[%d], speed[%d]\n", dcm->cfg->dir, dcm->cfg->speed);
00458 }
00459 
00464 void dc_motor_setPriority(int priority)
00465 {
00466     ASSERT(CONFIG_KERN);
00467     ASSERT(dc_motor);
00468     proc_setPri(dc_motor, priority);
00469 }
00470 
00475 void dc_motor_init(void)
00476 {
00477     ASSERT(CONFIG_KERN);
00478 
00479     MOTOR_DC_INIT();
00480 
00481     #if (CONFIG_KERN_PREEMPT && CONFIG_DC_MOTOR_USE_SEM)
00482         sem_init(&dc_motor_sem);
00483     #endif
00484 
00485     //Create a dc motor poll process
00486     dc_motor = proc_new_with_name("DC_Motor", dc_motor_poll, NULL, sizeof(dc_motor_poll_stack), dc_motor_poll_stack);
00487 }
00488