Control the speed and direction of a DC Motor using a L293D.

These are my first steps in the creation of some application for my arduino board. This will detail the experiments with DC motor control. Eventually a manual Xenon leveling system for my bike will be implemented.

First, my working setup:

[TBD: add picture of actual setup. Or someone tell me how to upload picture :-)]

The servo has been modified in such a manner that it can do a complete 360. And all internals have been removed.

Second, the source code.

  1.  
  2. //
  3. // This code creates a PWM signal that rotates a motor iether left or right.
  4. // PWN is used to reduce the voltage of the motor from 12V --> 5V
  5. // This allows the power regulation for the digital part to be very small (and simple)
  6. //
  7. // Simple schematic:
  8. //
  9. //                  +-------+                         VCC2=12V
  10. //                  | L293D |                         VCC1=5V
  11. //                  | |\    |
  12. // MOTOR_DIR -------+-| >---+------[(M)]-----+
  13. //                  | |/    |                |
  14. //                  |       |                |
  15. //                  | |\    |                |
  16. // MOTOR_PWM -------+-| >---+----------------+
  17. //                  | |/    |
  18. //                  |       |
  19. //                  +-------+
  20.  
  21. #include <avr/interrupt.h>
  22. #include <avr/io.h>  
  23.  
  24. // Make pulse width as such that average voltage=5V
  25. // Value 110 is experimantal.....
  26. // For testing purposes the value can be divided by two (2)
  27. #define MOTOR_SPEED 110/2
  28.  
  29. int LEDPIN = 13;          // Heartbeat led
  30. int MOTOR_DIR = 12;       // Non PWM pin for direction control
  31. int MOTOR_PWM = 11;       // PWM controlled pin.
  32.  
  33. int int_counter = 0;  
  34.  
  35. // We have ~32000 Overflows per second...  
  36. // Make led pulse to show its alive (heartbeat?)
  37. ISR(TIMER2_OVF_vect) {  
  38.   int_counter += 1;  
  39.   if (int_counter == 3200) {  
  40.     digitalWrite(LEDPIN, LOW);  
  41.   }
  42.   if (int_counter == 32000) {  
  43.     int_counter = 0;
  44.     digitalWrite(LEDPIN, HIGH);  
  45.   }  
  46. };  
  47.  
  48. // Turn the motor left.
  49. // If the motor needs to turn right after this call,
  50. // use motor_stop first!
  51. void motor_left() {
  52.   // Set the pulse width
  53.   OCR2A=MOTOR_SPEED;
  54.   // Set the directional bit to the correct value
  55.   digitalWrite(MOTOR_DIR, HIGH);
  56.   // Set output to PWM
  57.   TCCR2A |= ((1<<COM2A1) | (1<<COM2A0));
  58. }
  59.  
  60. // Turn the motor right.
  61. // If the motor needs to turn left after this call,
  62. // use motor_stop first!
  63. void motor_right() {
  64.   // Set the pulse width
  65.   OCR2A=MOTOR_SPEED;
  66.   // Set the directional bit to the correct value
  67.   digitalWrite(MOTOR_DIR, LOW);
  68.   // Set output to PWM (inverted of motor_left function)
  69.   TCCR2A |= ((1<<COM2A1) | (0<<COM2A0));
  70. }
  71.  
  72. // The following three commands should happen in the shortest time possible.
  73. // TODO: So optimisation is needed!
  74. void motor_stop() {
  75.   // Disconnect the PWM
  76.   TCCR2A &= ~((1<<COM2A1) | (1<<COM2A0));
  77.   // And put is all back to a safe 'LOW'
  78.   digitalWrite(MOTOR_DIR, LOW);
  79.   digitalWrite(MOTOR_PWM, LOW);  
  80. }
  81.  
  82. void setup() {  
  83.   Serial.begin(9600);
  84.   CLKPR=0;
  85.  
  86.   // Set the pins to output.
  87.   pinMode(LEDPIN, OUTPUT);
  88.   pinMode(MOTOR_DIR, OUTPUT);
  89.   pinMode(MOTOR_PWM, OUTPUT);
  90.   // And set these to a initial value to make sure.
  91.   digitalWrite(MOTOR_DIR, LOW);
  92.   digitalWrite(MOTOR_PWM, LOW);
  93.  
  94.  
  95.   // Use phase correct PWM Mode and set Output Compare mode
  96.   // Eventual PWM fequency will be ~30kHz, this is done to
  97.   // - minimize noise
  98.   // - minimize heat (suturation of motor --> L293=RIP)
  99.   TCCR2A = (0<<WGM21) | (1<<WGM20) | (0<<COM2A1) | (0<<COM2A0) | (0<<COM2B1) | (0<<COM2B0);
  100.   TCCR2B = (0<<WGM22) | (0<<CS22) | (0<<CS21) | (1<<CS20) | (0<<FOC2A) | (0<<FOC2B);
  101.   //Timer2 Overflow Interrupt Enable for the heartbeat
  102.   TIMSK2 |= (1<<TOIE2) | (0<<OCIE2A);
  103.   //sei();  // Not needed at this moment.
  104. }  
  105.  
  106. void loop() {  
  107.   motor_left();
  108.   delay(1000);
  109.   motor_stop();
  110.  
  111.   motor_right();
  112.   delay(1000);
  113.   motor_stop();
  114. }
  115.  
  116.  

Final, Considerations

  • I have chosen a very high frequency for the PWN output. The reasons for this are mentioned in the above source code.
  • The heartbeat can be removed by disabling the TOVF interrupt. Probably a lot of cycles are lost as a result of this heartbeat (20% ?)
  • Play around with the MOTOR_SPEED value. For me the value 110 worked ok.
  • More questions/additions needed. Let me know.

Share