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