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 :-)] Could someone share a link for this circuit?

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.