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>
27 //Flag variable if pwm was initialized and is ready to start.
28 static boolean_t pwm_initialized = FALSE;
30 /// Watchdog Task --------------------------------------------------------------
31 static boolean_t wdg_start = FALSE;
33 // Prepared command to be send on SPI.
34 // Default value is watchdog reset command.
35 uint16_t hbr_spi_wdg_tx = 0x03DB;
37 // Shadow variable of hbr_spi_wdg_tx
38 uint16_t hbr_spi_wdg_tx_shd = 0x03DB;
41 uint16_t hbr_spi_wdg_rx = 0;
43 // Shadow variable of hbr_spi_wdg_shd
44 uint16_t hbr_spi_wdg_rx_shd = 0;
46 // SPI communication result code (one of SPI_MSG_*)
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;
56 * @brief SPI callback function
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
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
69 int drv_hbr_spi_wdg_callback(struct spi_drv *ifc, int code,
70 struct spi_msg_head *msg)
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;
80 // SPI message format definition for watchdog reset command
81 spi_msg_head_t hbr_spi_wdg = {
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,
92 * Watchdog FreeRTOS Task function.
94 * Select appropriate spi address
95 * Initialize task timer
96 * Send watchdog keep-alive command each 10ms.
98 * @param[in] p Pointer to parameter, unused.
100 void drv_hbr_wdg_task(void *p)
104 ifc = spi_find_drv(NULL, 4);
111 portTickType xLastWakeTime;
112 xLastWakeTime = xTaskGetTickCount();
115 vTaskDelayUntil(&xLastWakeTime, (10 / portTICK_RATE_MS));
116 xSemaphoreTake(wdg_sync, portMAX_DELAY);
118 xLastWakeTime = xTaskGetTickCount();
122 spi_msg_rq_ins(ifc, &hbr_spi_wdg);
123 xSemaphoreGive(wdg_sync);
127 /// Watchdog API ---------------------------------------------------------------
128 static xTaskHandle wdg_handle = NULL;
131 * Start the watchdog task to send keep-alive commands periodically to H-Bridge.
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
137 int8_t drv_hbr_wdg_start()
143 // Task never started
144 if (wdg_handle == NULL ) {
145 wdg_sync = xSemaphoreCreateMutex();
146 if (wdg_sync == NULL )
148 if (xTaskCreate(drv_hbr_wdg_task,
149 (const signed char *)"hbr_wdg_task",
150 1024, NULL, 1, &wdg_handle) != pdPASS) {
156 // Task already created
157 vTaskResume(drv_hbr_wdg_task);
163 * Stop the watchdog task.
165 * @return SUCCESS if watchdog could be stopped.\n
166 * FAILURE if watchdog wasn't running.
168 int8_t drv_hbr_wdg_stop()
173 xSemaphoreTake(wdg_sync, portMAX_DELAY);
174 vTaskSuspend(drv_hbr_wdg_task);
175 xSemaphoreGive(wdg_sync);
179 /// H-Bridge API ---------------------------------------------------------------
181 * @brief Set PWM period and duty cycle to HBR_PWM pin
183 * Set period and dutycycle to HBR_PWM pin.
184 * Period is expected to be in us, duty cycle in percent of the period,
186 * If period is lower than 50us or duty greater than 100, function returns without having effect.
188 * @param[in] period Period of PWM in us
189 * @param[in] duty Width of duty in %
191 int8_t drv_hbr_pwm_set_signal(double period, uint32_t duty)
193 hetSIGNAL_t tmp_signal;
201 tmp_signal.duty = duty;
202 tmp_signal.period = period;
203 pwmSetSignal(hetRAM1, pwm0, tmp_signal);
205 pwm_initialized = TRUE;
211 * Start PWM on HBR_PWM pin
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.
216 * @return 0 if success, -1 when PWM was not yes set.
218 int8_t drv_hbr_pwm_start()
220 if (pwm_initialized) {
222 pwmStart(hetRAM1, pwm0);
232 * @brief Stop PWM on HBR_PWM pin
234 void drv_hbr_pwm_stop()
236 pwmStop(hetRAM1, pwm0);
239 void drv_hbr_pwm_set_duty(uint8_t percent)
241 // Don't mind doing range check, pwmSetDuty handles this in error free
243 pwmSetDuty(hetRAM1, pwm0, percent);
247 * @brief Get duty width of PWM on HBR_PWM pin
249 * @return Duty width of PWM in %
251 uint32_t drv_hbr_pwm_get_duty()
253 hetSIGNAL_t tmp_signal;
255 tmp_signal = pwmGetSignal(hetRAM1, pwm0);
256 return tmp_signal.duty;
260 * @brief Get period of PWM on HBR_PWM pin
262 * @return Period of PWM in us
264 double drv_hbr_pwm_get_period()
266 hetSIGNAL_t tmp_signal;
268 tmp_signal = pwmGetSignal(hetRAM1, pwm0);
269 return tmp_signal.period;
274 * @brief Set value to HBR_DIR pin.
276 * @param[in] direction If O, set hbr_dir to 0, otherwise to 1
278 void drv_hbr_set_dir(int direction)
280 dio_gpio_pin_set_value(*dio_gpio_pin_get_dsc(DIO_PIN_NAME_HBRDIR, -1), direction);
284 * @brief Get value of hbr_dir
286 * @return return 0 or 1 - the value of hbr_dir
288 int drv_hbr_get_dir()
290 return dio_gpio_pin_get_value(*dio_gpio_pin_get_dsc(DIO_PIN_NAME_HBRDIR, -1));
294 * @brief Set value to HBR_EN pin.
296 * @param[in] direction If O, set hbr_en to 0, otherwise to 1
298 void drv_hbr_set_en(int value)
300 dio_gpio_pin_set_value(*dio_gpio_pin_get_dsc(DIO_PIN_NAME_HBREN, -1), value);
304 * @brief Get value of HBR_EN
306 * @return return 0 or 1 - the value of hbr_en
310 return dio_gpio_pin_get_value(*dio_gpio_pin_get_dsc(DIO_PIN_NAME_HBREN, -1));