parent
0aa76da9f2
commit
38f480741f
|
@ -11,7 +11,8 @@ HW_LAYER_DRIVERS = \
|
|||
$(DRIVERS_DIR)/gpio/tle6240.c \
|
||||
$(DRIVERS_DIR)/gpio/tle8888.c \
|
||||
$(DRIVERS_DIR)/gpio/mc33972.c \
|
||||
$(DRIVERS_DIR)/gpio/mc33810.c
|
||||
$(DRIVERS_DIR)/gpio/mc33810.c \
|
||||
$(DRIVERS_DIR)/gpio/drv8860.c \
|
||||
|
||||
HW_LAYER_DRIVERS_CPP =
|
||||
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* drv8860.c
|
||||
*
|
||||
* DRV8860 Smart 8/16-Channel Low-Side Switch
|
||||
*
|
||||
* All channels are controlled via the serial interface (SPI).
|
||||
* <200 kHz SPI
|
||||
*
|
||||
* @date Apr 6, 2020
|
||||
*
|
||||
* @author andreika, (c) 2020
|
||||
* @author Andrey Belomutskiy, (c) 2012-2020
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "gpio/gpio_ext.h"
|
||||
#include "gpio/drv8860.h"
|
||||
#include "pin_repository.h"
|
||||
#include "os_util.h"
|
||||
|
||||
#if (BOARD_DRV8860_COUNT > 0)
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*==========================================================================*/
|
||||
|
||||
#define DRIVER_NAME "drv8860"
|
||||
|
||||
static bool drv_task_ready = false;
|
||||
|
||||
typedef enum {
|
||||
DRV8860_DISABLED = 0,
|
||||
DRV8860_WAIT_INIT,
|
||||
DRV8860_READY,
|
||||
DRV8860_FAILED
|
||||
} drv8860_drv_state;
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*==========================================================================*/
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* OS */
|
||||
SEMAPHORE_DECL(drv8860_wake, 10 /* or BOARD_DRV8860_COUNT ? */);
|
||||
static THD_WORKING_AREA(drv8860_thread_1_wa, 256);
|
||||
|
||||
/* Driver */
|
||||
struct drv8860_priv {
|
||||
const struct drv8860_config *cfg;
|
||||
/* cached output state - state last send to chip */
|
||||
uint16_t o_state_cached;
|
||||
/* state to be sended to chip */
|
||||
uint16_t o_state;
|
||||
|
||||
drv8860_drv_state drv_state;
|
||||
};
|
||||
|
||||
static struct drv8860_priv chips[BOARD_DRV8860_COUNT];
|
||||
|
||||
static const char* drv8860_pin_names[DRV8860_OUTPUTS] = {
|
||||
"drv8860.OUT1", "drv8860.OUT2", "drv8860.OUT3", "drv8860.OUT4",
|
||||
"drv8860.OUT5", "drv8860.OUT6", "drv8860.OUT7", "drv8860.OUT8",
|
||||
"drv8860.OUT9", "drv8860.OUT10", "drv8860.OUT11", "drv8860.OUT12",
|
||||
"drv8860.OUT13", "drv8860.OUT14", "drv8860.OUT15", "drv8860.OUT16",
|
||||
};
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*==========================================================================*/
|
||||
|
||||
static SPIDriver *get_bus(struct drv8860_priv *chip) {
|
||||
/* return non-const SPIDriver* from const struct cfg */
|
||||
return chip->cfg->spi_bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DRV8860 send routine.
|
||||
* @details Sends 8/16 bits. CS asserted before and released after transaction.
|
||||
*/
|
||||
|
||||
static void drv8860_spi_send(struct drv8860_priv *chip, uint16_t tx) {
|
||||
SPIDriver *spi = get_bus(chip);
|
||||
|
||||
/* Acquire ownership of the bus. */
|
||||
spiAcquireBus(spi);
|
||||
/* Setup transfer parameters. */
|
||||
spiStart(spi, &chip->cfg->spi_config);
|
||||
/* Slave Select assertion. */
|
||||
spiSelect(spi);
|
||||
/* Atomic transfer operations. */
|
||||
spiPolledExchange(spi, tx);
|
||||
/* Slave Select de-assertion. */
|
||||
spiUnselect(spi);
|
||||
/* Ownership release. */
|
||||
spiReleaseBus(spi);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DRV8860 send output data.
|
||||
*/
|
||||
|
||||
static void drv8860_update_outputs(struct drv8860_priv *chip) {
|
||||
/* TODO: lock? */
|
||||
|
||||
/* atomic */
|
||||
/* set value only for non-direct driven pins */
|
||||
drv8860_spi_send(chip, chip->o_state & 0xffff);
|
||||
|
||||
/* atomic */
|
||||
chip->o_state_cached = chip->o_state;
|
||||
|
||||
/* TODO: unlock? */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DRV8860 chip init.
|
||||
* @details Marks all used pins.
|
||||
* @todo: Checks direct io signals integrity, read initial diagnostic state.
|
||||
*/
|
||||
|
||||
static int drv8860_chip_init(struct drv8860_priv *chip) {
|
||||
/* upload pin states */
|
||||
drv8860_update_outputs(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DRV8860 chip driver wakeup.
|
||||
* @details Wake up driver. Will cause output register update.
|
||||
*/
|
||||
|
||||
static int drv8860_wake_driver(struct drv8860_priv *chip) {
|
||||
(void)chip;
|
||||
|
||||
/* Entering a reentrant critical zone.*/
|
||||
syssts_t sts = chSysGetStatusAndLockX();
|
||||
chSemSignalI(&drv8860_wake);
|
||||
/* Leaving the critical zone.*/
|
||||
chSysRestoreStatusX(sts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Driver thread. */
|
||||
/*==========================================================================*/
|
||||
|
||||
static THD_FUNCTION(drv8860_driver_thread, p) {
|
||||
int i;
|
||||
msg_t msg;
|
||||
|
||||
(void)p;
|
||||
|
||||
chRegSetThreadName(DRIVER_NAME);
|
||||
|
||||
while (1) {
|
||||
msg = chSemWaitTimeout(&drv8860_wake, TIME_MS2I(DRV8860_POLL_INTERVAL_MS));
|
||||
|
||||
/* should we care about msg == MSG_TIMEOUT? */
|
||||
(void)msg;
|
||||
|
||||
for (i = 0; i < BOARD_DRV8860_COUNT; i++) {
|
||||
struct drv8860_priv *chip;
|
||||
|
||||
chip = &chips[i];
|
||||
if ((chip->cfg == NULL) ||
|
||||
(chip->drv_state == DRV8860_DISABLED) ||
|
||||
(chip->drv_state == DRV8860_FAILED))
|
||||
continue;
|
||||
|
||||
drv8860_update_outputs(chip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* TODO: add IRQ support */
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*==========================================================================*/
|
||||
|
||||
int drv8860_writePad(void *data, unsigned int pin, int value) {
|
||||
struct drv8860_priv *chip;
|
||||
|
||||
if ((pin >= DRV8860_OUTPUTS) || (data == NULL))
|
||||
return -1;
|
||||
|
||||
chip = (struct drv8860_priv *)data;
|
||||
|
||||
/* TODO: lock */
|
||||
if (value)
|
||||
chip->o_state |= (1 << pin);
|
||||
else
|
||||
chip->o_state &= ~(1 << pin);
|
||||
/* TODO: unlock */
|
||||
drv8860_wake_driver(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
brain_pin_diag_e drv8860_getDiag(void *data, unsigned int pin) {
|
||||
// todo: implement diag
|
||||
return PIN_OK;
|
||||
}
|
||||
|
||||
int drv8860_init(void * data) {
|
||||
int ret;
|
||||
struct drv8860_priv *chip;
|
||||
|
||||
chip = (struct drv8860_priv *)data;
|
||||
|
||||
ret = drv8860_chip_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->drv_state = DRV8860_READY;
|
||||
|
||||
if (!drv_task_ready) {
|
||||
chThdCreateStatic(drv8860_thread_1_wa, sizeof(drv8860_thread_1_wa),
|
||||
NORMALPRIO + 1, drv8860_driver_thread, NULL);
|
||||
drv_task_ready = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int drv8860_deinit(void *data) {
|
||||
(void)data;
|
||||
|
||||
/* TODO: set all pins to inactive state, stop task? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gpiochip_ops drv8860_ops = {
|
||||
.writePad = drv8860_writePad,
|
||||
.readPad = NULL, /* chip outputs only */
|
||||
.getDiag = drv8860_getDiag,
|
||||
.init = drv8860_init,
|
||||
.deinit = drv8860_deinit,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief DRV8860 driver add.
|
||||
* @details Checks for valid config
|
||||
*/
|
||||
|
||||
int drv8860_add(unsigned int index, const struct drv8860_config *cfg) {
|
||||
int i;
|
||||
int ret;
|
||||
struct drv8860_priv *chip;
|
||||
|
||||
/* no config or no such chip */
|
||||
if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_DRV8860_COUNT))
|
||||
return -1;
|
||||
|
||||
/* check for valid cs.
|
||||
* TODO: remove this check? CS can be driven by SPI */
|
||||
//if (cfg->spi_config.ssport == NULL)
|
||||
// return -1;
|
||||
|
||||
chip = &chips[index];
|
||||
|
||||
/* already initted? */
|
||||
if (chip->cfg != NULL)
|
||||
return -1;
|
||||
|
||||
chip->cfg = cfg;
|
||||
chip->o_state = 0;
|
||||
chip->o_state_cached = 0;
|
||||
chip->drv_state = DRV8860_WAIT_INIT;
|
||||
|
||||
/* register, return gpio chip base */
|
||||
ret = gpiochip_register(DRIVER_NAME, &drv8860_ops, DRV8860_OUTPUTS, chip);
|
||||
|
||||
/* set default pin names, board init code can rewrite */
|
||||
gpiochips_setPinNames(ret, drv8860_pin_names);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* BOARD_DRV8860_COUNT > 0 */
|
||||
|
||||
int drv8860_add(unsigned int index, const struct drv8860_config *cfg) {
|
||||
(void)index; (void)cfg;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* BOARD_DRV8860_COUNT */
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* drv8860.h
|
||||
*
|
||||
* DRV8860 Smart 8/16-Channel Low-Side Switch
|
||||
*
|
||||
* @date Apr 6, 2020
|
||||
*
|
||||
* @author andreika, (c) 2020
|
||||
* @author Andrey Belomutskiy, (c) 2012-2020
|
||||
*/
|
||||
|
||||
#ifndef HW_LAYER_DRV8860_H_
|
||||
#define HW_LAYER_DRV8860_H_
|
||||
|
||||
#include "efifeatures.h"
|
||||
#include <hal.h>
|
||||
|
||||
#define DRV8860_OUTPUTS 16
|
||||
|
||||
/* TODO: add irq support */
|
||||
#define DRV8860_POLL_INTERVAL_MS 500
|
||||
|
||||
struct drv8860_config {
|
||||
SPIDriver *spi_bus;
|
||||
SPIConfig spi_config;
|
||||
struct {
|
||||
ioportid_t port;
|
||||
uint_fast8_t pad;
|
||||
} reset;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int drv8860_add(unsigned int index, const struct drv8860_config *cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* HW_LAYER_DRV8860_H_ */
|
Loading…
Reference in New Issue