]> 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 4d7a898d54073d45f9a4975fb4ac058da903c481..08b4efcfbf8c4878ce48385b4a953e9bcf1b0cf2 100644 (file)
@@ -1,67 +1,54 @@
-/*
- * 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
@@ -77,76 +64,171 @@ void hbr_wdg_task(void *pvParameters) {
  *
  * @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);
 }
 
 /**
@@ -154,10 +236,12 @@ void hbr_pwm_stop() {
  *
  * @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;
 }
 
 /**
@@ -165,10 +249,12 @@ uint32_t hbr_pwm_get_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;
 
 }
 
@@ -177,8 +263,9 @@ double hbr_pwm_get_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);
 }
 
 /**
@@ -186,8 +273,9 @@ void hbr_set_dir(int 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;
 }
 
 /**
@@ -195,15 +283,17 @@ int hbr_get_dir() {
  *
  *  @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;
 }