]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lib.git/blob - rpp/src/drv/hbridge.c
c5da440086d2bc5a42a39a3bd2ecac716bcbbd1b
[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_tms570.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         .dev = SPIDEV_L99H01,
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         portTickType xLastWakeTime;
104         xLastWakeTime = xTaskGetTickCount();
105
106         while (TRUE) {
107                 vTaskDelayUntil(&xLastWakeTime, (10 / portTICK_RATE_MS));
108                 xSemaphoreTake(wdg_sync, portMAX_DELAY);
109                 if (wdg_start) {
110                         xLastWakeTime = xTaskGetTickCount();
111                         wdg_start = FALSE;
112                 }
113                 else
114                         spi_msg_rq_ins(&hbr_spi_wdg);
115                 xSemaphoreGive(wdg_sync);
116         }
117 }
118
119 /// Watchdog API ---------------------------------------------------------------
120 static xTaskHandle wdg_handle = NULL;
121
122 /**
123  * Start the watchdog task to send keep-alive commands periodically to H-Bridge.
124  *
125  * @return SUCCESS if watchdog could be started.\n
126  *         -RPP_EBUSY if watchdog is running already
127  *         -RPP_ENOMEM if the task could not be created
128  */
129 int8_t drv_hbr_wdg_start()
130 {
131         if (wdg_start)
132                 return -RPP_EBUSY;
133
134         wdg_start = TRUE;
135         // Task never started
136         if (wdg_handle == NULL ) {
137                 wdg_sync = xSemaphoreCreateMutex();
138                 if (wdg_sync == NULL )
139                         return -RPP_ENOMEM;
140                 if (xTaskCreate(drv_hbr_wdg_task,
141                                                 "hbr_wdg_task",
142                                                 1024, NULL, 1, &wdg_handle) != pdPASS) {
143                         wdg_start = FALSE;
144                         return -RPP_ENOMEM;
145                 }
146         }
147         else
148                 // Task already created
149                 vTaskResume(drv_hbr_wdg_task);
150
151         return SUCCESS;
152 }
153
154 /**
155  * Stop the watchdog task.
156  *
157  * @return SUCCESS if watchdog could be stopped.\n
158  *         FAILURE if watchdog wasn't running.
159  */
160 int8_t drv_hbr_wdg_stop()
161 {
162         if (!wdg_start)
163                 return FAILURE;
164
165         xSemaphoreTake(wdg_sync, portMAX_DELAY);
166         vTaskSuspend(drv_hbr_wdg_task);
167         xSemaphoreGive(wdg_sync);
168         return SUCCESS;
169 }
170
171 /// H-Bridge API ---------------------------------------------------------------
172 /**
173  * @brief Set PWM period and duty cycle to HBR_PWM pin
174  *
175  * Set period and dutycycle to HBR_PWM pin.
176  * Period is expected to be in us, duty cycle in percent of the period,
177  *
178  * If period is lower than 50us or duty greater than 100, function returns without having effect.
179  *
180  * @param[in]   period      Period of PWM in us
181  * @param[in]   duty        Width of duty in %
182  */
183 int8_t drv_hbr_pwm_set_signal(double period, uint32_t duty)
184 {
185         hetSIGNAL_t tmp_signal;
186
187         if (duty > 100)
188                 return FAILURE;
189
190         if (period < 50)
191                 return FAILURE;
192
193         tmp_signal.duty   = duty;
194         tmp_signal.period = period;
195         pwmSetSignal(hetRAM1, pwm0, tmp_signal);
196
197         pwm_initialized = TRUE;
198
199         return SUCCESS;
200 }
201
202 /**
203  * Start PWM on HBR_PWM pin
204  *
205  * If PWM was set previously by hbr_pwm_set_signal function, this procedure starts it.
206  * Otherwise function returns and PWM is not started.
207  *
208  * @return  0 if success, -1 when PWM was not yes set.
209  */
210 int8_t drv_hbr_pwm_start()
211 {
212         if (pwm_initialized) {
213
214                 pwmStart(hetRAM1, pwm0);
215                 return SUCCESS;
216
217         }
218         else
219
220                 return FAILURE;
221 }
222
223 /**
224  * @brief Stop PWM on HBR_PWM pin
225  */
226 void drv_hbr_pwm_stop()
227 {
228         pwmStop(hetRAM1, pwm0);
229 }
230
231 void drv_hbr_pwm_set_duty(uint8_t percent)
232 {
233         // Don't mind doing range check, pwmSetDuty handles this in error free
234         // manner.
235         pwmSetDuty(hetRAM1, pwm0, percent);
236 }
237
238 /**
239  * @brief Get duty width of PWM on HBR_PWM pin
240  *
241  * @return      Duty width of PWM in %
242  */
243 uint32_t drv_hbr_pwm_get_duty()
244 {
245         hetSIGNAL_t tmp_signal;
246
247         tmp_signal = pwmGetSignal(hetRAM1, pwm0);
248         return tmp_signal.duty;
249 }
250
251 /**
252  * @brief Get period of PWM on HBR_PWM pin
253  *
254  * @return      Period of PWM in us
255  */
256 double drv_hbr_pwm_get_period()
257 {
258         hetSIGNAL_t tmp_signal;
259
260         tmp_signal = pwmGetSignal(hetRAM1, pwm0);
261         return tmp_signal.period;
262
263 }
264
265 /**
266  *  @brief  Set value to HBR_DIR pin.
267  *
268  *  @param[in]  direction   If O, set hbr_dir to 0, otherwise to 1
269  */
270 void drv_hbr_set_dir(int direction)
271 {
272         gio_tab_set(PIN_HBRDIR, !!direction);
273 }
274
275 /**
276  *  @brief  Get value of hbr_dir
277  *
278  *  @return return 0 or 1 - the value of hbr_dir
279  */
280 int drv_hbr_get_dir()
281 {
282         return gio_tab_get(PIN_HBRDIR) ? 1 : 0;
283 }
284
285 /**
286  *  @brief  Set value to HBR_EN pin.
287  *
288  *  @param[in]  direction   If O, set hbr_en to 0, otherwise to 1
289  */
290 void drv_hbr_set_en(int value)
291 {
292         gio_tab_set(PIN_HBREN, !!value);
293 }
294
295 /**
296  *  @brief  Get value of HBR_EN
297  *
298  *  @return return 0 or 1 - the value of hbr_en
299  */
300 int drv_hbr_get_en()
301 {
302         return gio_tab_get(PIN_HBREN) ? 1 : 0;
303 }