b48a41129a5cdf72e9766a2bb39202a5e50e4d4d
[fw/altos] / src / drivers / ao_servo.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19 #include "ao_servo.h"
20 #include "ao_pwm.h"
21 #include "ao_exti.h"
22
23 static uint8_t  limit_wakeup;
24
25 static void
26 ao_limit_isr(void)
27 {
28         ao_wakeup(&limit_wakeup);
29 }
30
31 static void
32 ao_limit_enable(uint8_t dir)
33 {
34         if (dir == AO_SERVO_FORE)
35                 ao_exti_enable(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT);
36         else
37                 ao_exti_enable(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT);
38 }
39
40 static void
41 ao_limit_disable(uint8_t dir)
42 {
43         if (dir == AO_SERVO_FORE)
44                 ao_exti_disable(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT);
45         else
46                 ao_exti_disable(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT);
47 }
48
49 static uint8_t
50 ao_limit_get(uint8_t dir)
51 {
52         if (dir == AO_SERVO_FORE)
53                 return !ao_gpio_get(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT, PIN);
54         else
55                 return !ao_gpio_get(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT, PIN);
56 }
57
58 void
59 ao_servo_run(uint16_t speed, uint8_t dir, uint16_t timeout)
60 {
61         printf ("speed %d dir %d\n", speed, dir);
62
63         /* Turn on the motor */
64         ao_gpio_set(AO_SERVO_DIR_PORT, AO_SERVO_DIR_BIT, AO_SERVO_DIR_PIN, dir);
65         ao_pwm_set(AO_SERVO_SPEED_PWM, speed);
66
67         /* Wait until the limit sensor is triggered */
68         ao_arch_block_interrupts();
69         ao_limit_enable(dir);
70         while (!ao_limit_get(dir))
71                 if (ao_sleep_for(&limit_wakeup, timeout))
72                         break;
73         ao_limit_disable(dir);
74         ao_arch_release_interrupts();
75
76         /* Turn off the motor */
77         ao_pwm_set(AO_SERVO_SPEED_PWM, 0);
78 }
79
80 #define init_limit(p,b) do {                                            \
81                 ao_enable_port(p);                                      \
82                 ao_exti_setup(p, b,                                     \
83                               AO_EXTI_MODE_PULL_UP|AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, \
84                               ao_limit_isr);                            \
85         } while (0)
86
87
88 static void
89 ao_servo_cmd(void)
90 {
91         uint8_t dir;
92         uint16_t speed;
93
94         ao_cmd_decimal();
95         dir = ao_cmd_lex_u32;
96         ao_cmd_decimal();
97         speed = ao_cmd_lex_u32;
98         if (ao_cmd_status != ao_cmd_success)
99                 return;
100
101         printf("Run servo %d\n", dir);
102         ao_servo_run(speed, dir, AO_MS_TO_TICKS(200));
103 }
104
105 static const struct ao_cmds ao_servo_cmds[] = {
106         { ao_servo_cmd, "S <dir> <speed>\0Run servo in indicated direction" },
107         { 0, NULL },
108 };
109
110
111 void
112 ao_servo_init(void)
113 {
114         init_limit(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT);
115         init_limit(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT);
116         ao_enable_output(AO_SERVO_DIR_PORT, AO_SERVO_DIR_BIT, AO_SERVO_DIR_PIN, 0);
117         ao_cmd_register(&ao_servo_cmds[0]);
118 }