/* Copyright 2016 Benjamin Vedder benjamin@vedder.se This file is part of the VESC firmware. The VESC firmware 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 3 of the License, or (at your option) any later version. The VESC firmware 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 . */ #include "conf_general.h" #ifdef HW_HAS_DRV8316 #include "drv8316.h" #include "ch.h" #include "hal.h" #include "stm32f4xx_conf.h" #include "utils_math.h" #include "terminal.h" #include "commands.h" #include #include #include "mc_interface.h" // Private functions static uint16_t spi_exchange(uint16_t x); static void spi_transfer(uint16_t *in_buf, const uint16_t *out_buf, int length); static void spi_begin(void); static void spi_end(void); static void spi_delay(void); static void terminal_read_reg(int argc, const char **argv); static void terminal_write_reg(int argc, const char **argv); // static void terminal_set_oc_adj(int argc, const char **argv); static void terminal_print_faults(int argc, const char **argv); static void terminal_reset_faults(int argc, const char **argv); // Private variables static char m_fault_print_buffer[120]; static mutex_t m_spi_mutex; void drv8316_init(void) { chMtxObjectInit(&m_spi_mutex); // Initialise DRV8316 SPI palSetPadMode(DRV8316_MISO_GPIO, DRV8316_MISO_PIN, PAL_MODE_INPUT_PULLUP); palSetPadMode(DRV8316_SCK_GPIO, DRV8316_SCK_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPadMode(DRV8316_CS_GPIO, DRV8316_CS_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPadMode(DRV8316_MOSI_GPIO, DRV8316_MOSI_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPad(DRV8316_CS_GPIO, DRV8316_CS_PIN); // Chip select rests high chThdSleepMilliseconds(100); // Unlock all registers for write access uint8_t UNLOCK_ALL_REGISTERS = 0b011; uint16_t ctrl_reg_1_value = drv8316_read_reg(DRV8316_CTRL_REG_1); ctrl_reg_1_value &= 0xF8; // Keeps reserved bits (3-7) the same ctrl_reg_1_value |= UNLOCK_ALL_REGISTERS; // Command all registers to unlock drv8316_write_reg(DRV8316_CTRL_REG_1, ctrl_reg_1_value); chThdSleepMilliseconds(10); terminal_register_command_callback( "drv8316_read_reg", "Read a register from the DRV8316 and print it.", "[reg]", terminal_read_reg); terminal_register_command_callback( "drv8316_write_reg", "Write to a DRV8316 register.", "[reg] [hexvalue]", terminal_write_reg); terminal_register_command_callback( "drv8316_print_faults", "Print all current DRV8316 faults.", 0, terminal_print_faults); terminal_register_command_callback( "drv8316_reset_faults", "Resets all DRV8316 faults.", 0, terminal_reset_faults); } // /** // * Read the fault codes of the DRV8316. // */ uint32_t drv8316_read_faults(void) { uint8_t ic_status = drv8316_read_reg(DRV8316_IC_STATUS_REG); uint16_t status_reg1 = drv8316_read_reg(DRV8316_STATUS_REG_1); uint32_t status_reg2 = drv8316_read_reg(DRV8316_STATUS_REG_2) & 0x7F; // Bit 7 is reserved return ic_status | (status_reg1 << 8) | (status_reg2 << 16); } // /** // * Reset all latched faults. // */ void drv8316_reset_faults(void) { int reg = drv8316_read_reg(DRV8316_CTRL_REG_2); uint8_t CLR_FLT = 0b1; reg |= CLR_FLT; drv8316_write_reg(DRV8316_CTRL_REG_2, reg); } char *drv8316_faults_to_string(unsigned long faults) { commands_printf("faults: %d", faults); if (faults == 0) { strcpy(m_fault_print_buffer, "No DRV8316 faults"); } else { uint8_t ic_status_faults = faults & 0xFF; uint8_t status_reg1_faults = (faults >> 8) & 0xFF; uint8_t status_reg2_faults = (faults >> 16) & 0xFF; commands_printf("ic_status_faults: %x", ic_status_faults); commands_printf("status_reg1_faults: %x", status_reg1_faults); commands_printf("status_reg2_faults: %x", status_reg2_faults); strcpy(m_fault_print_buffer, "|"); // IC Status Register if (ic_status_faults & DRV8316_IC_FAULT_BUCK_FLT) { strcat(m_fault_print_buffer, " DRV8316_IC_FAULT_BUCK_FLT |"); } if (ic_status_faults & DRV8316_IC_FAULT_SPI_FLT) { strcat(m_fault_print_buffer, " DRV8316_IC_FAULT_SPI_FLT |"); } if (ic_status_faults & DRV8316_IC_FAULT_OCP) { strcat(m_fault_print_buffer, " DRV8316_IC_FAULT_OCP |"); } if (ic_status_faults & DRV8316_IC_FAULT_NPOR) { strcat(m_fault_print_buffer, " DRV8316_IC_FAULT_NPOR |"); } if (ic_status_faults & DRV8316_IC_FAULT_OVP) { strcat(m_fault_print_buffer, " DRV8316_IC_FAULT_OVP |"); } if (ic_status_faults & DRV8316_IC_FAULT_OT) { strcat(m_fault_print_buffer, " DRV8316_IC_FAULT_OT |"); } if (ic_status_faults & DRV8316_IC_FAULT_DEVICE_FLT) { strcat(m_fault_print_buffer, " DRV8316_IC_FAULT_DEVICE_FLT |"); } // Status Register 1 Faults if (status_reg1_faults & DRV8316_FAULT_STS_1_OTW) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OTW |"); } if (status_reg1_faults & DRV8316_FAULT_STS_1_OTS) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OTS |"); } if (status_reg1_faults & DRV8316_FAULT_STS_1_OCP_HC) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OCP_HC |"); } if (status_reg1_faults & DRV8316_FAULT_STS_1_OCP_LC) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OCP_LC |"); } if (status_reg1_faults & DRV8316_FAULT_STS_1_OCP_HB) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OCP_HB |"); } if (status_reg1_faults & DRV8316_FAULT_STS_1_OCP_LB) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OCP_LB |"); } if (status_reg1_faults & DRV8316_FAULT_STS_1_OCP_HA) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OCP_HA |"); } if (status_reg1_faults & DRV8316_FAULT_STS_1_OCP_LA) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_1_OCP_LA |"); } // Status Register 2 Faults if (status_reg2_faults & DRV8316_FAULT_STS_2_OTP_ERR) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_2_OTP_ERR |"); } if (status_reg2_faults & DRV8316_FAULT_STS_2_BUCK_OCP) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_2_BUCK_OCP |"); } if (status_reg2_faults & DRV8316_FAULT_STS_2_BUCK_UV) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_2_BUCK_UV |"); } if (status_reg2_faults & DRV8316_FAULT_STS_2_VCP_UV) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_2_VCP_UV |"); } if (status_reg2_faults & DRV8316_FAULT_STS_2_SPI_PARITY) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_2_SPI_PARITY |"); } if (status_reg2_faults & DRV8316_FAULT_STS_2_SPI_SCLK_FLT) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_2_SPI_SCLK_FLT |"); } if (status_reg2_faults & DRV8316_FAULT_STS_2_SPI_ADDR_FLT) { strcat(m_fault_print_buffer, " DRV8316_FAULT_STS_2_SPI_ADDR_FLT |"); } } return m_fault_print_buffer; } unsigned int drv8316_read_reg(int reg) { uint16_t out = 0; out |= (1 << 15); out |= (reg & 0x3F) << 9; uint16_t parity = out; parity ^= parity >> 8; parity ^= parity >> 4; parity ^= parity >> 2; parity ^= parity >> 1; out |= (parity & 1) << 8; chMtxLock(&m_spi_mutex); if (reg != 0) { spi_begin(); spi_exchange(out); spi_end(); } spi_begin(); uint16_t res = spi_exchange(out); spi_end(); chMtxUnlock(&m_spi_mutex); return res; } void drv8316_write_reg(uint8_t reg, uint8_t data) { uint16_t out = 0; out |= (data & 0xFF); out |= (reg & 0x3F) << 9; uint16_t parity = out; parity ^= parity >> 8; parity ^= parity >> 4; parity ^= parity >> 2; parity ^= parity >> 1; out |= (parity & 1) << 8; chMtxLock(&m_spi_mutex); spi_begin(); spi_exchange(out); spi_end(); chMtxUnlock(&m_spi_mutex); } // Software SPI static uint16_t spi_exchange(uint16_t x) { uint16_t rx; spi_transfer(&rx, &x, 1); return rx; } static void spi_transfer(uint16_t *in_buf, const uint16_t *out_buf, int length) { for (int i = 0; i < length; i++) { uint16_t send = out_buf ? out_buf[i] : 0xFFFF; uint16_t receive = 0; for (int bit = 0; bit < 16; bit++) { palWritePad(DRV8316_MOSI_GPIO, DRV8316_MOSI_PIN, send >> 15); send <<= 1; palSetPad(DRV8316_SCK_GPIO, DRV8316_SCK_PIN); spi_delay(); palClearPad(DRV8316_SCK_GPIO, DRV8316_SCK_PIN); int samples = 0; samples += palReadPad(DRV8316_MISO_GPIO, DRV8316_MISO_PIN); __NOP(); samples += palReadPad(DRV8316_MISO_GPIO, DRV8316_MISO_PIN); __NOP(); samples += palReadPad(DRV8316_MISO_GPIO, DRV8316_MISO_PIN); __NOP(); samples += palReadPad(DRV8316_MISO_GPIO, DRV8316_MISO_PIN); __NOP(); samples += palReadPad(DRV8316_MISO_GPIO, DRV8316_MISO_PIN); receive <<= 1; if (samples > 2) { receive |= 1; } spi_delay(); } if (in_buf) { in_buf[i] = receive; } } } static void spi_begin(void) { spi_delay(); #ifdef DRV8316_CS_GPIO2 if (mc_interface_motor_now() == 2) { palClearPad(DRV8316_CS_GPIO2, DRV8316_CS_PIN2); } else { palClearPad(DRV8316_CS_GPIO, DRV8316_CS_PIN); } #else palClearPad(DRV8316_CS_GPIO, DRV8316_CS_PIN); #endif spi_delay(); } static void spi_end(void) { spi_delay(); #ifdef DRV8316_CS_GPIO2 if (mc_interface_motor_now() == 2) { palSetPad(DRV8316_CS_GPIO2, DRV8316_CS_PIN2); } else { palSetPad(DRV8316_CS_GPIO, DRV8316_CS_PIN); } #else palSetPad(DRV8316_CS_GPIO, DRV8316_CS_PIN); #endif spi_delay(); } static void spi_delay(void) { for (volatile int i = 0; i < 40; i++) { __NOP(); } } static void terminal_read_reg(int argc, const char **argv) { if (argc == 2) { int reg = -1; sscanf(argv[1], "%d", ®); if (reg >= 0) { unsigned int res = drv8316_read_reg(reg); char bl[9]; char bh[9]; utils_byte_to_binary((res >> 8) & 0xFF, bh); utils_byte_to_binary(res & 0xFF, bl); commands_printf("Reg 0x%02x: %s %s (0x%04x)\n", reg, bh, bl, res); } else { commands_printf("Invalid argument(s).\n"); } } else { commands_printf("This command requires one argument.\n"); } } static void terminal_write_reg(int argc, const char **argv) { if (argc == 3) { int reg = -1; int val = -1; sscanf(argv[1], "%d", ®); sscanf(argv[2], "%x", &val); if (reg >= 0 && val >= 0) { drv8316_write_reg(reg, val); unsigned int res = drv8316_read_reg(reg); char bl[9]; char bh[9]; utils_byte_to_binary((res >> 8) & 0xFF, bh); utils_byte_to_binary(res & 0xFF, bl); commands_printf("New reg value 0x%02x: %s %s (0x%04x)\n", reg, bh, bl, res); } else { commands_printf("Invalid argument(s).\n"); } } else { commands_printf("This command requires two arguments.\n"); } } // static void terminal_set_oc_adj(int argc, const char **argv) { // if (argc == 2) { // int val = -1; // sscanf(argv[1], "%d", &val); // if (val >= 0 && val < 32) { // drv8316_set_oc_adj(val); // unsigned int res = drv8316_read_reg(5); // char bl[9]; // char bh[9]; // utils_byte_to_binary((res >> 8) & 0xFF, bh); // utils_byte_to_binary(res & 0xFF, bl); // commands_printf("New reg value 0x%02x: %s %s (0x%04x)\n", 2, bh, bl, res); // } else { // commands_printf("Invalid argument(s).\n"); // } // } else { // commands_printf("This command requires one argument.\n"); // } // } static void terminal_print_faults(int argc, const char **argv) { (void)argc; (void)argv; commands_printf(drv8316_faults_to_string(drv8316_read_faults())); } static void terminal_reset_faults(int argc, const char **argv) { (void)argc; (void)argv; drv8316_reset_faults(); commands_printf("Reset all faults"); } #endif