]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lib.git/blobdiff - rpp/src/drv/hbridge.c
hbr: Check and simplify watchdog code
[pes-rpp/rpp-lib.git] / rpp / src / drv / hbridge.c
index e0a17e295c6b520ae969157a508a22934a413159..08b4efcfbf8c4878ce48385b4a953e9bcf1b0cf2 100644 (file)
@@ -1,21 +1,13 @@
-/* Copyright (C) 2013 Czech Technical University in Prague
+/* Copyright (C) 2013, 2015, 2016 Czech Technical University in Prague
  *
  * Authors:
  *     - Michal Horn
  *     - Carlos Jenkins <carlos@jenkins.co.cr>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 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.
  *
  * File : ain.c
  * Abstract:
 // 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 pwm was initialized and is ready to start.
 static boolean_t pwm_initialized = FALSE;
 
-
 /// Watchdog Task --------------------------------------------------------------
-static boolean_t wdg_running = FALSE;
-
+static boolean_t wdg_start = FALSE;
 
 // Prepared command to be send on SPI.
 // Default value is watchdog reset command.
-uint16_t hbr_spi_wdg_tx = 0x03DB;
-
-// Shadow variable of hbr_spi_wdg_tx
-uint16_t hbr_spi_wdg_tx_shd = 0x03DB;
+static const uint8_t hbr_spi_wdg_tx[2] = { 0x03, 0xDB };
 
 // Response from SPI.
-uint16_t hbr_spi_wdg_rx = 0;
+static uint8_t hbr_spi_wdg_rx[2] = {0};
 
 // Shadow variable of hbr_spi_wdg_shd
-uint16_t hbr_spi_wdg_rx_shd = 0;
+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
@@ -67,26 +64,25 @@ uint16_t hbr_spi_wdg_rx_shd = 0;
  *
  * @return  always zero
  */
-int drv_hbr_spi_wdg_callback(struct spi_drvifc, int code,
-                         struct spi_msg_head* msg)
+int drv_hbr_spi_wdg_callback(struct spi_drv *ifc, int code,
+                                                        struct spi_msg *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;
+       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_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 = drv_hbr_spi_wdg_callback,
-        .private = 1
+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
 };
 
 /**
@@ -100,26 +96,22 @@ spi_msg_head_t hbr_spi_wdg = {
  */
 void drv_hbr_wdg_task(void *p)
 {
-    spi_drv_t* ifc;
-    ifc = spi_find_drv(NULL, 4);
-
-    if(ifc == NULL) {
-        wdg_running = FALSE;
-        vTaskDelete(NULL);
-    }
-
-    portTickType xLastWakeTime;
-    xLastWakeTime = xTaskGetTickCount();
-
-    while(TRUE) {
-        if(wdg_running) {
-            spi_msg_rq_ins(ifc, &hbr_spi_wdg);
-        }
-        vTaskDelayUntil(&xLastWakeTime, (10 / portTICK_RATE_MS));
-    }
+       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;
 
@@ -127,38 +119,34 @@ static xTaskHandle wdg_handle = NULL;
  * Start the watchdog task to send keep-alive commands periodically to H-Bridge.
  *
  * @return SUCCESS if watchdog could be started.\n
- *         FAILURE if H-Bridge not setup (call drv_hbr_set_signal() first), the
- *                 watchdog is running already or the task could not be created.
+ *         -RPP_EBUSY if watchdog is running already
+ *         -RPP_ENOMEM if the task could not be created
  */
 int8_t drv_hbr_wdg_start()
 {
-    if(!pwm_initialized) {
-        return FAILURE;
-    }
-
-    if(wdg_running) {
-        return FAILURE;
-    }
-
-    // Task already created
-    if(wdg_handle != NULL) {
-        wdg_running = TRUE;
-
-    // Task never started
-    } else {
-        wdg_running = TRUE;
-        if(xTaskCreate(drv_hbr_wdg_task,
-                       (const signed char *)"hbr_wdg_task",
-                       256, NULL, 1, &wdg_handle) != pdPASS) {
-            wdg_running = FALSE;
-            return FAILURE;
-        }
-    }
-
-    return SUCCESS;
+       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.
  *
@@ -167,19 +155,15 @@ int8_t drv_hbr_wdg_start()
  */
 int8_t drv_hbr_wdg_stop()
 {
-    if(!pwm_initialized) {
-        return FAILURE;
-    }
+       if (!wdg_start)
+               return FAILURE;
 
-    if(!wdg_running) {
-        return FAILURE;
-    }
-
-    wdg_running = FALSE;
-    return SUCCESS;
+       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
@@ -194,26 +178,23 @@ int8_t drv_hbr_wdg_stop()
  */
 int8_t drv_hbr_pwm_set_signal(double period, uint32_t duty)
 {
-    hetSIGNAL_t tmp_signal;
+       hetSIGNAL_t tmp_signal;
 
-    if(duty > 100) {
-        return FAILURE;
-    }
+       if (duty > 100)
+               return FAILURE;
 
-    if(period < 50) {
-        return FAILURE;
-    }
+       if (period < 50)
+               return FAILURE;
 
-    tmp_signal.duty   = duty;
-    tmp_signal.period = period;
-    pwmSetSignal(hetRAM1, pwm0, tmp_signal);
+       tmp_signal.duty   = duty;
+       tmp_signal.period = period;
+       pwmSetSignal(hetRAM1, pwm0, tmp_signal);
 
-    pwm_initialized = TRUE;
+       pwm_initialized = TRUE;
 
-    return SUCCESS;
+       return SUCCESS;
 }
 
-
 /**
  * Start PWM on HBR_PWM pin
  *
@@ -224,29 +205,30 @@ int8_t drv_hbr_pwm_set_signal(double period, uint32_t duty)
  */
 int8_t drv_hbr_pwm_start()
 {
-    if (pwm_initialized) {
+       if (pwm_initialized) {
 
-        pwmStart(hetRAM1, pwm0);
-        return SUCCESS;
+               pwmStart(hetRAM1, pwm0);
+               return SUCCESS;
 
-    } else {
+       }
+       else
 
-        return FAILURE;
-    }
+               return FAILURE;
 }
 
 /**
  * @brief Stop PWM on HBR_PWM pin
  */
-void drv_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);
+       // Don't mind doing range check, pwmSetDuty handles this in error free
+       // manner.
+       pwmSetDuty(hetRAM1, pwm0, percent);
 }
 
 /**
@@ -254,10 +236,12 @@ void drv_hbr_pwm_set_duty(uint8_t percent)
  *
  * @return      Duty width of PWM in %
  */
-uint32_t drv_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;
 }
 
 /**
@@ -265,10 +249,12 @@ uint32_t drv_hbr_pwm_get_duty() {
  *
  * @return      Period of PWM in us
  */
-double drv_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;
 
 }
 
@@ -277,8 +263,9 @@ double drv_hbr_pwm_get_period() {
  *
  *  @param[in]  direction   If O, set hbr_dir to 0, otherwise to 1
  */
-void drv_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);
 }
 
 /**
@@ -286,8 +273,9 @@ void drv_hbr_set_dir(int direction) {
  *
  *  @return return 0 or 1 - the value of hbr_dir
  */
-int drv_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;
 }
 
 /**
@@ -295,8 +283,9 @@ int drv_hbr_get_dir() {
  *
  *  @param[in]  direction   If O, set hbr_en to 0, otherwise to 1
  */
-void drv_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);
 }
 
 /**
@@ -304,6 +293,7 @@ void drv_hbr_set_en(int value) {
  *
  *  @return return 0 or 1 - the value of hbr_en
  */
-int drv_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;
 }