-/*
- * hbridge.c
+/* Copyright (C) 2013, 2015, 2016 Czech Technical University in Prague
*
- * Created on: 9.11.2012
- * Author: Michal Horn
+ * Authors:
+ * - Michal Horn
+ * - Carlos Jenkins <carlos@jenkins.co.cr>
*
- * This file contains functions to control H-bridge.
+ * This document contains proprietary information belonging to Czech
+ * Technical University in Prague. Passing on and copying of this
+ * document, and communication of its contents is not permitted
+ * without prior written authorization.
*
- * Periodical hbr watchdog reset command sending once it is sent manualy.
- * PWM for HBR control
+ * File : ain.c
+ * Abstract:
+ * RPP driver implementation for HBR.
+ *
+ * References:
+ * hbridge.h
*/
-//TODO: HBR_DIR and HBR_EN toggling
-
-//#include "drv_hbridge.h"
+// This file contains functions to control H-bridge.
+// A keep-alive watchdog is implemented.
+// PWM is available for HBR control.
#include "drv/drv.h"
+#include <os/semphr.h>
+#include "drv/spi_tms570.h"
+#include "drv/gio_tab.h"
-/** Flag variable if watchdog command has been sent one before **/
-static char hbr_spi_initialized;
-/** Prepared command to be send on SPI, default value is watchdog reset command **/
-uint16_t hbr_spi_wdg_tx = 0x03DB;
-/** Response from SPI **/
-uint16_t hbr_spi_wdg_rx = 0;
-/** Shadow variable of command being send on SPI, default value is watchdog reset command **/
-uint16_t hbr_spi_wdg_tx_shd = 0x03DB;
-/** Response from SPI **/
-uint16_t hbr_spi_wdg_rx_shd = 0;
-/** Flag variable if pwm was initialized and is ready to start **/
-uint8_t hbr_pwm_initialized = 0;
-/** SPI message format definition for watchdog reset command **/
-spi_msg_head_t hbr_spi_wdg = {
- .flags = 0,
- .addr = 0,
- .rq_len = 2,
- .tx_buf = (uint8_t*) &hbr_spi_wdg_tx_shd,
- .rx_buf = (uint8_t*) &hbr_spi_wdg_rx_shd,
- .callback = hbr_spi_wdg_callback,
- .private = 1
-};
+//Flag variable if pwm was initialized and is ready to start.
+static boolean_t pwm_initialized = FALSE;
-/**
- * @brief Watchdog reset task function
- *
- * Select appropriate spi address
- * Initialize task timer
- * Send watchdog reset command each 10ms
- *
- * @param[in] pvParameters Pointer to parameter, not used
- */
-/* FIXME Move this implementation and header somewhere else.
-void hbr_wdg_task(void *pvParameters) {
- spi_drv_t *ifc;
- ifc = spi_find_drv(NULL, 4);
- if (ifc == NULL)
- return;
-
- portTickType xLastWakeTime;
- xLastWakeTime = xTaskGetTickCount();
- for( ;; ) {
- spi_msg_rq_ins(ifc, &hbr_spi_wdg);
- vTaskDelayUntil( &xLastWakeTime, ( 10 / portTICK_RATE_MS ) );
- }
-}
-*/
+/// Watchdog Task --------------------------------------------------------------
+static boolean_t wdg_start = FALSE;
+
+// Prepared command to be send on SPI.
+// Default value is watchdog reset command.
+static const uint8_t hbr_spi_wdg_tx[2] = { 0x03, 0xDB };
+
+// Response from SPI.
+static uint8_t hbr_spi_wdg_rx[2] = {0};
+
+// Shadow variable of hbr_spi_wdg_shd
+static uint8_t hbr_spi_wdg_rx_shd[2] = {0};
+
+// SPI communication result code (one of SPI_MSG_*)
+int hbr_spi_code = 0;
+
+// In certain situation the H-Bridge stops working without signaling that in
+// its status byte. This happens when the watchdog message is sent
+// approximately 25us after asserting EN signal. To prevent this situation we
+// have to synchronize EN signal with watchdog thread.
+static xSemaphoreHandle wdg_sync;
/**
* @brief SPI callback function
*
* @return always zero
*/
-int hbr_spi_wdg_callback (struct spi_drv * ifc, int code, struct spi_msg_head * msg) {
- if (code == SPI_MSG_FINISHED) {
- hbr_spi_wdg_rx = hbr_spi_wdg_rx_shd;
- hbr_spi_wdg_tx_shd = hbr_spi_wdg_tx;
- }
- return 0;
+int drv_hbr_spi_wdg_callback(struct spi_drv *ifc, int code,
+ struct spi_msg *msg)
+{
+ hbr_spi_code = code;
+ if (code == SPI_MSG_FINISHED) {
+ memcpy(hbr_spi_wdg_rx, hbr_spi_wdg_rx_shd, sizeof(hbr_spi_wdg_rx));
+ }
+ return 0;
+}
+
+// SPI message format definition for watchdog reset command
+spi_msg_t hbr_spi_wdg = {
+ .flags = 0,
+ .dev = SPIDEV_L99H01,
+ .rq_len = sizeof(hbr_spi_wdg_tx),
+ .tx_buf = hbr_spi_wdg_tx,
+ .rx_buf = hbr_spi_wdg_rx_shd,
+ .callback = drv_hbr_spi_wdg_callback,
+ .private = 1
+};
+
+/**
+ * Watchdog FreeRTOS Task function.
+ *
+ * Select appropriate spi address
+ * Initialize task timer
+ * Send watchdog keep-alive command each 10ms.
+ *
+ * @param[in] p Pointer to parameter, unused.
+ */
+void drv_hbr_wdg_task(void *p)
+{
+ portTickType xLastWakeTime;
+ xLastWakeTime = xTaskGetTickCount();
+
+ while (TRUE) {
+ vTaskDelayUntil(&xLastWakeTime, (10 / portTICK_RATE_MS));
+ xSemaphoreTake(wdg_sync, portMAX_DELAY);
+ if (wdg_start) {
+ xLastWakeTime = xTaskGetTickCount();
+ wdg_start = FALSE;
+ }
+ else
+ spi_msg_rq_ins(&hbr_spi_wdg);
+ xSemaphoreGive(wdg_sync);
+ }
}
+/// Watchdog API ---------------------------------------------------------------
+static xTaskHandle wdg_handle = NULL;
+
/**
- * @brief Start periodically sending watchdog reset commands
+ * Start the watchdog task to send keep-alive commands periodically to H-Bridge.
*
- * If watchdog command is going to be send for the first time, create new thread and start sending the command each 10ms
+ * @return SUCCESS if watchdog could be started.\n
+ * -RPP_EBUSY if watchdog is running already
+ * -RPP_ENOMEM if the task could not be created
+ */
+int8_t drv_hbr_wdg_start()
+{
+ if (wdg_start)
+ return -RPP_EBUSY;
+
+ wdg_start = TRUE;
+ // Task never started
+ if (wdg_handle == NULL ) {
+ wdg_sync = xSemaphoreCreateMutex();
+ if (wdg_sync == NULL )
+ return -RPP_ENOMEM;
+ if (xTaskCreate(drv_hbr_wdg_task,
+ "hbr_wdg_task",
+ 1024, NULL, 1, &wdg_handle) != pdPASS) {
+ wdg_start = FALSE;
+ return -RPP_ENOMEM;
+ }
+ }
+ else
+ // Task already created
+ vTaskResume(drv_hbr_wdg_task);
+
+ return SUCCESS;
+}
+
+/**
+ * Stop the watchdog task.
*
- * @return SPI response or -1 when thread creation failed.
+ * @return SUCCESS if watchdog could be stopped.\n
+ * FAILURE if watchdog wasn't running.
*/
-int hbr_spi_wdg_transfer() {
- if (!hbr_spi_initialized) {
- hbr_spi_initialized = 1;
- // FIXME Move this implementation and header somewhere else
- //if (xTaskCreate(hbr_wdg_task, (const signed char *)"HBR_WDG", 300, NULL, 1, NULL ) != pdPASS) {
- // return -1;
- //}
- }
- return hbr_spi_wdg_rx;
+int8_t drv_hbr_wdg_stop()
+{
+ if (!wdg_start)
+ return FAILURE;
+
+ xSemaphoreTake(wdg_sync, portMAX_DELAY);
+ vTaskSuspend(drv_hbr_wdg_task);
+ xSemaphoreGive(wdg_sync);
+ return SUCCESS;
}
+/// H-Bridge API ---------------------------------------------------------------
/**
* @brief Set PWM period and duty cycle to HBR_PWM pin
*
* Set period and dutycycle to HBR_PWM pin.
* Period is expected to be in us, duty cycle in percent of the period,
*
- * If period is lower than 1 or duty greater than 100, function returns without having effect.
+ * If period is lower than 50us or duty greater than 100, function returns without having effect.
*
* @param[in] period Period of PWM in us
* @param[in] duty Width of duty in %
*/
-void hbr_pwm_set_signal(double period, uint32_t duty) {
- hetSIGNAL_t tmp_signal;
- if (duty > 100) return;
- if (period < 1) return;
- tmp_signal.duty = duty;
- tmp_signal.period = period;
- pwmSetSignal(hetRAM1, pwm0, tmp_signal);
- hbr_pwm_initialized = 1;
+int8_t drv_hbr_pwm_set_signal(double period, uint32_t duty)
+{
+ hetSIGNAL_t tmp_signal;
+
+ if (duty > 100)
+ return FAILURE;
+
+ if (period < 50)
+ return FAILURE;
+
+ tmp_signal.duty = duty;
+ tmp_signal.period = period;
+ pwmSetSignal(hetRAM1, pwm0, tmp_signal);
+
+ pwm_initialized = TRUE;
+
+ return SUCCESS;
}
/**
- * @brief Start PWM on HBR_PWM pin
+ * Start PWM on HBR_PWM pin
*
* If PWM was set previously by hbr_pwm_set_signal function, this procedure starts it.
* Otherwise function returns and PWM is not started.
*
* @return 0 if success, -1 when PWM was not yes set.
*/
-int hbr_pwm_start() {
- if (hbr_pwm_initialized) {
- pwmStart(hetRAM1, pwm0);
- return 0;
- }
- else {
- return -1;
- }
+int8_t drv_hbr_pwm_start()
+{
+ if (pwm_initialized) {
+
+ pwmStart(hetRAM1, pwm0);
+ return SUCCESS;
+
+ }
+ else
+
+ return FAILURE;
}
/**
* @brief Stop PWM on HBR_PWM pin
*/
-void hbr_pwm_stop() {
- pwmStop(hetRAM1, pwm0);
+void drv_hbr_pwm_stop()
+{
+ pwmStop(hetRAM1, pwm0);
+}
+
+void drv_hbr_pwm_set_duty(uint8_t percent)
+{
+ // Don't mind doing range check, pwmSetDuty handles this in error free
+ // manner.
+ pwmSetDuty(hetRAM1, pwm0, percent);
}
/**
*
* @return Duty width of PWM in %
*/
-uint32_t hbr_pwm_get_duty() {
- hetSIGNAL_t tmp_signal;
- tmp_signal = pwmGetSignal(hetRAM1, pwm0);
- return tmp_signal.duty;
+uint32_t drv_hbr_pwm_get_duty()
+{
+ hetSIGNAL_t tmp_signal;
+
+ tmp_signal = pwmGetSignal(hetRAM1, pwm0);
+ return tmp_signal.duty;
}
/**
*
* @return Period of PWM in us
*/
-double hbr_pwm_get_period() {
- hetSIGNAL_t tmp_signal;
- tmp_signal = pwmGetSignal(hetRAM1, pwm0);
- return tmp_signal.period;
+double drv_hbr_pwm_get_period()
+{
+ hetSIGNAL_t tmp_signal;
+
+ tmp_signal = pwmGetSignal(hetRAM1, pwm0);
+ return tmp_signal.period;
}
*
* @param[in] direction If O, set hbr_dir to 0, otherwise to 1
*/
-void hbr_set_dir(int direction) {
- hal_gpio_pin_set_value(PIN_DSC_HBRDIR, direction);
+void drv_hbr_set_dir(int direction)
+{
+ gio_tab_set(PIN_HBRDIR, !!direction);
}
/**
*
* @return return 0 or 1 - the value of hbr_dir
*/
-int hbr_get_dir() {
- return hal_gpio_pin_get_value(PIN_DSC_HBRDIR);
+int drv_hbr_get_dir()
+{
+ return gio_tab_get(PIN_HBRDIR) ? 1 : 0;
}
/**
*
* @param[in] direction If O, set hbr_en to 0, otherwise to 1
*/
-void hbr_set_en(int value) {
- hal_gpio_pin_set_value(PIN_DSC_HBREN, value);
+void drv_hbr_set_en(int value)
+{
+ gio_tab_set(PIN_HBREN, !!value);
}
/**
- * @brief Get value of hbr_eþn
+ * @brief Get value of HBR_EN
*
* @return return 0 or 1 - the value of hbr_en
*/
-int hbr_get_en() {
- return hal_gpio_pin_get_value(PIN_DSC_HBREN);
+int drv_hbr_get_en()
+{
+ return gio_tab_get(PIN_HBREN) ? 1 : 0;
}