]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lib.git/blob - rpp/src/drv/hbridge.c
cc126979c7fce84d8b3460803761a44d81443665
[pes-rpp/rpp-lib.git] / rpp / src / drv / hbridge.c
1 /* Copyright (C) 2013, 2015 Czech Technical University in Prague
2  *
3  * Authors:
4  *     - Michal Horn
5  *     - Carlos Jenkins <carlos@jenkins.co.cr>
6  *
7  * This document contains proprietary information belonging to Czech
8  * Technical University in Prague. Passing on and copying of this
9  * document, and communication of its contents is not permitted
10  * without prior written authorization.
11  *
12  * File : ain.c
13  * Abstract:
14  *     RPP driver implementation for HBR.
15  *
16  * References:
17  *     hbridge.h
18  */
19
20 // This file contains functions to control H-bridge.
21 // A keep-alive watchdog is implemented.
22 // PWM is available for HBR control.
23 #include "drv/drv.h"
24 #include <os/semphr.h>
25 #include "drv/spi.h"
26 #include "drv/gio_tab.h"
27
28 //Flag variable if pwm was initialized and is ready to start.
29 static boolean_t pwm_initialized = FALSE;
30
31 /// Watchdog Task --------------------------------------------------------------
32 static boolean_t wdg_start = FALSE;
33
34 // Prepared command to be send on SPI.
35 // Default value is watchdog reset command.
36 uint16_t hbr_spi_wdg_tx = 0x03DB;
37
38 // Shadow variable of hbr_spi_wdg_tx
39 uint16_t hbr_spi_wdg_tx_shd = 0x03DB;
40
41 // Response from SPI.
42 uint16_t hbr_spi_wdg_rx = 0;
43
44 // Shadow variable of hbr_spi_wdg_shd
45 uint16_t hbr_spi_wdg_rx_shd = 0;
46
47 // SPI communication result code (one of SPI_MSG_*)
48 int hbr_spi_code = 0;
49
50 // In certain situation the H-Bridge stops working without signaling that in
51 // its status byte.  This happens when the watchdog message is sent
52 // approximately 25us after asserting EN signal.  To prevent this situation we
53 // have to synchronize EN signal with watchdog thread.
54 static xSemaphoreHandle wdg_sync;
55
56 /**
57  * @brief   SPI callback function
58  *
59  * This function is called each time SPI transfer finishes.
60  * Gets response and prepare command for next sending.
61  * Copy response from shadow variable,
62  * Copy prepared command to shadow variable
63  *
64  * @param[in]   ifc     Pointer to SPI driver structure
65  * @param[in]   code    SPI transfer status code
66  * @param[in]   msg     Pointer to message definition structure
67  *
68  * @return  always zero
69  */
70 int drv_hbr_spi_wdg_callback(struct spi_drv *ifc, int code,
71                                                          struct spi_msg *msg)
72 {
73         hbr_spi_code = code;
74         if (code == SPI_MSG_FINISHED) {
75                 hbr_spi_wdg_rx = hbr_spi_wdg_rx_shd;
76                 hbr_spi_wdg_tx_shd = hbr_spi_wdg_tx;
77         }
78         return 0;
79 }
80
81 // SPI message format definition for watchdog reset command
82 spi_msg_t hbr_spi_wdg = {
83         .flags = 0,
84         .addr = 0,
85         .rq_len = 2,
86         .tx_buf = (uint8_t *)&hbr_spi_wdg_tx_shd,
87         .rx_buf = (uint8_t *)&hbr_spi_wdg_rx_shd,
88         .callback = drv_hbr_spi_wdg_callback,
89         .private = 1
90 };
91
92 /**
93  * Watchdog FreeRTOS Task function.
94  *
95  * Select appropriate spi address
96  * Initialize task timer
97  * Send watchdog keep-alive command each 10ms.
98  *
99  * @param[in]   p    Pointer to parameter, unused.
100  */
101 void drv_hbr_wdg_task(void *p)
102 {
103         spi_drv_t *ifc;
104
105         ifc = spi_find_drv(NULL, 4);
106
107         if (ifc == NULL) {
108                 wdg_start = FALSE;
109                 vTaskDelete(NULL);
110         }
111
112         portTickType xLastWakeTime;
113         xLastWakeTime = xTaskGetTickCount();
114
115         while (TRUE) {
116                 vTaskDelayUntil(&xLastWakeTime, (10 / portTICK_RATE_MS));
117                 xSemaphoreTake(wdg_sync, portMAX_DELAY);
118                 if (wdg_start) {
119                         xLastWakeTime = xTaskGetTickCount();
120                         wdg_start = FALSE;
121                 }
122                 else
123                         spi_msg_rq_ins(ifc, &hbr_spi_wdg);
124                 xSemaphoreGive(wdg_sync);
125         }
126 }
127
128 /// Watchdog API ---------------------------------------------------------------
129 static xTaskHandle wdg_handle = NULL;
130
131 /**
132  * Start the watchdog task to send keep-alive commands periodically to H-Bridge.
133  *
134  * @return SUCCESS if watchdog could be started.\n
135  *         -RPP_EBUSY if watchdog is running already
136  *         -RPP_ENOMEM if the task could not be created
137  */
138 int8_t drv_hbr_wdg_start()
139 {
140         if (wdg_start)
141                 return -RPP_EBUSY;
142
143         wdg_start = TRUE;
144         // Task never started
145         if (wdg_handle == NULL ) {
146                 wdg_sync = xSemaphoreCreateMutex();
147                 if (wdg_sync == NULL )
148                         return -RPP_ENOMEM;
149                 if (xTaskCreate(drv_hbr_wdg_task,
150                                                 "hbr_wdg_task",
151                                                 1024, NULL, 1, &wdg_handle) != pdPASS) {
152                         wdg_start = FALSE;
153                         return -RPP_ENOMEM;
154                 }
155         }
156         else
157                 // Task already created
158                 vTaskResume(drv_hbr_wdg_task);
159
160         return SUCCESS;
161 }
162
163 /**
164  * Stop the watchdog task.
165  *
166  * @return SUCCESS if watchdog could be stopped.\n
167  *         FAILURE if watchdog wasn't running.
168  */
169 int8_t drv_hbr_wdg_stop()
170 {
171         if (!wdg_start)
172                 return FAILURE;
173
174         xSemaphoreTake(wdg_sync, portMAX_DELAY);
175         vTaskSuspend(drv_hbr_wdg_task);
176         xSemaphoreGive(wdg_sync);
177         return SUCCESS;
178 }
179
180 /// H-Bridge API ---------------------------------------------------------------
181 /**
182  * @brief Set PWM period and duty cycle to HBR_PWM pin
183  *
184  * Set period and dutycycle to HBR_PWM pin.
185  * Period is expected to be in us, duty cycle in percent of the period,
186  *
187  * If period is lower than 50us or duty greater than 100, function returns without having effect.
188  *
189  * @param[in]   period      Period of PWM in us
190  * @param[in]   duty        Width of duty in %
191  */
192 int8_t drv_hbr_pwm_set_signal(double period, uint32_t duty)
193 {
194         hetSIGNAL_t tmp_signal;
195
196         if (duty > 100)
197                 return FAILURE;
198
199         if (period < 50)
200                 return FAILURE;
201
202         tmp_signal.duty   = duty;
203         tmp_signal.period = period;
204         pwmSetSignal(hetRAM1, pwm0, tmp_signal);
205
206         pwm_initialized = TRUE;
207
208         return SUCCESS;
209 }
210
211 /**
212  * Start PWM on HBR_PWM pin
213  *
214  * If PWM was set previously by hbr_pwm_set_signal function, this procedure starts it.
215  * Otherwise function returns and PWM is not started.
216  *
217  * @return  0 if success, -1 when PWM was not yes set.
218  */
219 int8_t drv_hbr_pwm_start()
220 {
221         if (pwm_initialized) {
222
223                 pwmStart(hetRAM1, pwm0);
224                 return SUCCESS;
225
226         }
227         else
228
229                 return FAILURE;
230 }
231
232 /**
233  * @brief Stop PWM on HBR_PWM pin
234  */
235 void drv_hbr_pwm_stop()
236 {
237         pwmStop(hetRAM1, pwm0);
238 }
239
240 void drv_hbr_pwm_set_duty(uint8_t percent)
241 {
242         // Don't mind doing range check, pwmSetDuty handles this in error free
243         // manner.
244         pwmSetDuty(hetRAM1, pwm0, percent);
245 }
246
247 /**
248  * @brief Get duty width of PWM on HBR_PWM pin
249  *
250  * @return      Duty width of PWM in %
251  */
252 uint32_t drv_hbr_pwm_get_duty()
253 {
254         hetSIGNAL_t tmp_signal;
255
256         tmp_signal = pwmGetSignal(hetRAM1, pwm0);
257         return tmp_signal.duty;
258 }
259
260 /**
261  * @brief Get period of PWM on HBR_PWM pin
262  *
263  * @return      Period of PWM in us
264  */
265 double drv_hbr_pwm_get_period()
266 {
267         hetSIGNAL_t tmp_signal;
268
269         tmp_signal = pwmGetSignal(hetRAM1, pwm0);
270         return tmp_signal.period;
271
272 }
273
274 /**
275  *  @brief  Set value to HBR_DIR pin.
276  *
277  *  @param[in]  direction   If O, set hbr_dir to 0, otherwise to 1
278  */
279 void drv_hbr_set_dir(int direction)
280 {
281         gio_tab_set(PIN_HBRDIR, !!direction);
282 }
283
284 /**
285  *  @brief  Get value of hbr_dir
286  *
287  *  @return return 0 or 1 - the value of hbr_dir
288  */
289 int drv_hbr_get_dir()
290 {
291         return gio_tab_get(PIN_HBRDIR) ? 1 : 0;
292 }
293
294 /**
295  *  @brief  Set value to HBR_EN pin.
296  *
297  *  @param[in]  direction   If O, set hbr_en to 0, otherwise to 1
298  */
299 void drv_hbr_set_en(int value)
300 {
301         gio_tab_set(PIN_HBREN, !!value);
302 }
303
304 /**
305  *  @brief  Get value of HBR_EN
306  *
307  *  @return return 0 or 1 - the value of hbr_en
308  */
309 int drv_hbr_get_en()
310 {
311         return gio_tab_get(PIN_HBREN) ? 1 : 0;
312 }