1 /* Copyright (C) 2013, 2015 Czech Technical University in Prague
5 * - Carlos Jenkins <carlos@jenkins.co.cr>
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.
14 * RPP driver implementation for HBR.
20 // This file contains functions to control H-bridge.
21 // A keep-alive watchdog is implemented.
22 // PWM is available for HBR control.
24 #include <os/semphr.h>
25 #include "drv/spi_tms570.h"
26 #include "drv/gio_tab.h"
28 //Flag variable if pwm was initialized and is ready to start.
29 static boolean_t pwm_initialized = FALSE;
31 /// Watchdog Task --------------------------------------------------------------
32 static boolean_t wdg_start = FALSE;
34 // Prepared command to be send on SPI.
35 // Default value is watchdog reset command.
36 uint16_t hbr_spi_wdg_tx = 0x03DB;
38 // Shadow variable of hbr_spi_wdg_tx
39 uint16_t hbr_spi_wdg_tx_shd = 0x03DB;
42 uint16_t hbr_spi_wdg_rx = 0;
44 // Shadow variable of hbr_spi_wdg_shd
45 uint16_t hbr_spi_wdg_rx_shd = 0;
47 // SPI communication result code (one of SPI_MSG_*)
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;
57 * @brief SPI callback function
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
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
70 int drv_hbr_spi_wdg_callback(struct spi_drv *ifc, int 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;
81 // SPI message format definition for watchdog reset command
82 spi_msg_t hbr_spi_wdg = {
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,
93 * Watchdog FreeRTOS Task function.
95 * Select appropriate spi address
96 * Initialize task timer
97 * Send watchdog keep-alive command each 10ms.
99 * @param[in] p Pointer to parameter, unused.
101 void drv_hbr_wdg_task(void *p)
103 portTickType xLastWakeTime;
104 xLastWakeTime = xTaskGetTickCount();
107 vTaskDelayUntil(&xLastWakeTime, (10 / portTICK_RATE_MS));
108 xSemaphoreTake(wdg_sync, portMAX_DELAY);
110 xLastWakeTime = xTaskGetTickCount();
114 spi_msg_rq_ins(&hbr_spi_wdg);
115 xSemaphoreGive(wdg_sync);
119 /// Watchdog API ---------------------------------------------------------------
120 static xTaskHandle wdg_handle = NULL;
123 * Start the watchdog task to send keep-alive commands periodically to H-Bridge.
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
129 int8_t drv_hbr_wdg_start()
135 // Task never started
136 if (wdg_handle == NULL ) {
137 wdg_sync = xSemaphoreCreateMutex();
138 if (wdg_sync == NULL )
140 if (xTaskCreate(drv_hbr_wdg_task,
142 1024, NULL, 1, &wdg_handle) != pdPASS) {
148 // Task already created
149 vTaskResume(drv_hbr_wdg_task);
155 * Stop the watchdog task.
157 * @return SUCCESS if watchdog could be stopped.\n
158 * FAILURE if watchdog wasn't running.
160 int8_t drv_hbr_wdg_stop()
165 xSemaphoreTake(wdg_sync, portMAX_DELAY);
166 vTaskSuspend(drv_hbr_wdg_task);
167 xSemaphoreGive(wdg_sync);
171 /// H-Bridge API ---------------------------------------------------------------
173 * @brief Set PWM period and duty cycle to HBR_PWM pin
175 * Set period and dutycycle to HBR_PWM pin.
176 * Period is expected to be in us, duty cycle in percent of the period,
178 * If period is lower than 50us or duty greater than 100, function returns without having effect.
180 * @param[in] period Period of PWM in us
181 * @param[in] duty Width of duty in %
183 int8_t drv_hbr_pwm_set_signal(double period, uint32_t duty)
185 hetSIGNAL_t tmp_signal;
193 tmp_signal.duty = duty;
194 tmp_signal.period = period;
195 pwmSetSignal(hetRAM1, pwm0, tmp_signal);
197 pwm_initialized = TRUE;
203 * Start PWM on HBR_PWM pin
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.
208 * @return 0 if success, -1 when PWM was not yes set.
210 int8_t drv_hbr_pwm_start()
212 if (pwm_initialized) {
214 pwmStart(hetRAM1, pwm0);
224 * @brief Stop PWM on HBR_PWM pin
226 void drv_hbr_pwm_stop()
228 pwmStop(hetRAM1, pwm0);
231 void drv_hbr_pwm_set_duty(uint8_t percent)
233 // Don't mind doing range check, pwmSetDuty handles this in error free
235 pwmSetDuty(hetRAM1, pwm0, percent);
239 * @brief Get duty width of PWM on HBR_PWM pin
241 * @return Duty width of PWM in %
243 uint32_t drv_hbr_pwm_get_duty()
245 hetSIGNAL_t tmp_signal;
247 tmp_signal = pwmGetSignal(hetRAM1, pwm0);
248 return tmp_signal.duty;
252 * @brief Get period of PWM on HBR_PWM pin
254 * @return Period of PWM in us
256 double drv_hbr_pwm_get_period()
258 hetSIGNAL_t tmp_signal;
260 tmp_signal = pwmGetSignal(hetRAM1, pwm0);
261 return tmp_signal.period;
266 * @brief Set value to HBR_DIR pin.
268 * @param[in] direction If O, set hbr_dir to 0, otherwise to 1
270 void drv_hbr_set_dir(int direction)
272 gio_tab_set(PIN_HBRDIR, !!direction);
276 * @brief Get value of hbr_dir
278 * @return return 0 or 1 - the value of hbr_dir
280 int drv_hbr_get_dir()
282 return gio_tab_get(PIN_HBRDIR) ? 1 : 0;
286 * @brief Set value to HBR_EN pin.
288 * @param[in] direction If O, set hbr_en to 0, otherwise to 1
290 void drv_hbr_set_en(int value)
292 gio_tab_set(PIN_HBREN, !!value);
296 * @brief Get value of HBR_EN
298 * @return return 0 or 1 - the value of hbr_en
302 return gio_tab_get(PIN_HBREN) ? 1 : 0;