-/* 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
*
* @return always zero
*/
-int drv_hbr_spi_wdg_callback(struct spi_drv* ifc, 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
};
/**
*/
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;
* 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.
*
*/
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
*/
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
*
*/
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);
}
/**
*
* @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;
}
/**
*
* @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;
}
*
* @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);
}
/**
*
* @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;
}
/**
*
* @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);
}
/**
*
* @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;
}