2019-05-03 18:13:25 -07:00
|
|
|
/*
|
|
|
|
* @file mc33816.cpp
|
|
|
|
*
|
|
|
|
* The NXP MC33816 is a programmable gate driver IC for precision solenoid control applications.
|
|
|
|
*
|
2019-05-08 07:58:43 -07:00
|
|
|
*
|
|
|
|
* For MC33816 vs PT2000 differences see
|
|
|
|
* https://www.nxp.com/docs/en/application-note/AN5203.pdf
|
|
|
|
*
|
2019-05-03 18:13:25 -07:00
|
|
|
* @date May 3, 2019
|
2020-01-07 21:02:40 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2020
|
2019-05-03 18:13:25 -07:00
|
|
|
*/
|
|
|
|
|
2019-05-03 18:38:34 -07:00
|
|
|
#include "global.h"
|
2019-05-03 18:13:25 -07:00
|
|
|
|
|
|
|
#if EFI_MC33816
|
|
|
|
|
2019-05-03 18:38:34 -07:00
|
|
|
#include "mc33816.h"
|
|
|
|
#include "engine_configuration.h"
|
|
|
|
#include "efi_gpio.h"
|
|
|
|
#include "hardware.h"
|
2019-05-03 18:54:38 -07:00
|
|
|
#include "mc33816_data.h"
|
|
|
|
#include "mpu_util.h"
|
2019-05-03 18:38:34 -07:00
|
|
|
|
|
|
|
EXTERN_CONFIG;
|
|
|
|
|
|
|
|
static OutputPin chipSelect;
|
2019-06-06 17:12:03 -07:00
|
|
|
static OutputPin resetB;
|
|
|
|
static OutputPin driven;
|
2019-05-03 18:38:34 -07:00
|
|
|
|
2020-04-13 16:01:22 -07:00
|
|
|
static bool flag0before = false;
|
|
|
|
static bool flag0after = false;
|
|
|
|
|
2020-02-25 18:35:21 -08:00
|
|
|
static unsigned short mcChipId;
|
|
|
|
static Logging* logger;
|
|
|
|
|
2019-05-03 18:38:34 -07:00
|
|
|
static SPIConfig spiCfg = { .circular = false,
|
|
|
|
.end_cb = NULL,
|
|
|
|
.ssport = NULL,
|
|
|
|
.sspad = 0,
|
2019-05-03 18:54:38 -07:00
|
|
|
.cr1 =
|
|
|
|
SPI_CR1_16BIT_MODE |
|
|
|
|
SPI_CR1_MSTR |
|
2019-05-03 18:38:34 -07:00
|
|
|
//SPI_CR1_BR_1 // 5MHz
|
2019-06-06 17:12:03 -07:00
|
|
|
SPI_CR1_CPHA | SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2 | SPI_CR1_SPE,
|
|
|
|
.cr2 = SPI_CR2_SSOE};
|
2019-05-03 18:38:34 -07:00
|
|
|
|
|
|
|
static SPIDriver *driver;
|
|
|
|
|
2020-04-14 12:05:33 -07:00
|
|
|
static bool validateChipId() {
|
|
|
|
return (mcChipId >> 8) == 0x9D;
|
|
|
|
}
|
2019-05-03 18:38:34 -07:00
|
|
|
|
2019-05-03 18:13:25 -07:00
|
|
|
static void showStats() {
|
2020-04-11 18:48:04 -07:00
|
|
|
// x9D is product code or something, and 43 is the revision?
|
2020-04-14 12:05:33 -07:00
|
|
|
scheduleMsg(logger, "MC %x %s", mcChipId, validateChipId() ? "hooray!" : "not hooray :(");
|
2020-04-13 16:01:22 -07:00
|
|
|
|
|
|
|
if (CONFIG(mc33816_flag0) != GPIO_UNASSIGNED) {
|
|
|
|
scheduleMsg(logger, "flag0 before %d after %d", flag0before, flag0after);
|
2020-04-13 16:49:02 -07:00
|
|
|
|
|
|
|
scheduleMsg(logger, "flag0 right now %d", efiReadPin(CONFIG(mc33816_flag0)));
|
|
|
|
|
2020-04-13 16:01:22 -07:00
|
|
|
} else {
|
2020-04-14 12:05:33 -07:00
|
|
|
scheduleMsg(logger, "No flag0 pin selected");
|
2020-04-13 16:01:22 -07:00
|
|
|
}
|
2019-05-03 18:13:25 -07:00
|
|
|
}
|
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
// Mostly unused
|
|
|
|
unsigned short recv_16bit_spi() {
|
|
|
|
unsigned short ret;
|
|
|
|
//spiSelect(driver);
|
|
|
|
spiReceive(driver, 1, &ret);
|
|
|
|
//spiUnselect(driver);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This could be used to detect if check byte is wrong.. or use a FLAG after init
|
|
|
|
unsigned short txrx_16bit_spi(const unsigned short param) {
|
|
|
|
unsigned short ret;
|
|
|
|
//spiSelect(driver);
|
|
|
|
spiExchange(driver, 1, ¶m, &ret);
|
|
|
|
//spiUnselect(driver);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send 16bits
|
|
|
|
static void spi_writew(unsigned short param) {
|
|
|
|
//spiSelect(driver);
|
2019-05-03 18:38:34 -07:00
|
|
|
spiSend(driver, 1, ¶m);
|
2019-06-06 17:12:03 -07:00
|
|
|
//spiUnselect(driver);
|
|
|
|
}
|
|
|
|
|
2020-02-25 18:35:21 -08:00
|
|
|
static unsigned short readId() {
|
2019-06-06 17:12:03 -07:00
|
|
|
spiSelect(driver);
|
|
|
|
spi_writew(0xBAA1);
|
|
|
|
unsigned short ID = recv_16bit_spi();
|
|
|
|
spiUnselect(driver);
|
|
|
|
return ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setup_spi() {
|
|
|
|
spiSelect(driver);
|
|
|
|
// Select Channel command
|
|
|
|
spi_writew(0x7FE1);
|
|
|
|
// Common Page
|
|
|
|
spi_writew(0x0004);
|
|
|
|
|
|
|
|
|
|
|
|
// Configure SPI command
|
|
|
|
spi_writew(0x3901);
|
|
|
|
// Mode A + Watchdog timer full
|
|
|
|
//spi_writew(0x001F);
|
|
|
|
spi_writew(0x009F); // + fast slew rate on miso
|
2019-05-03 18:38:34 -07:00
|
|
|
spiUnselect(driver);
|
2019-05-03 18:13:25 -07:00
|
|
|
}
|
|
|
|
|
2020-04-13 21:57:19 -07:00
|
|
|
static bool check_flash() {
|
|
|
|
spiSelect(driver);
|
|
|
|
|
|
|
|
// ch1
|
|
|
|
// read (MSB=1) at location, and 1 word
|
|
|
|
spi_writew((0x8000 | 0x100 << 5) + 1);
|
|
|
|
if (!(recv_16bit_spi() & (1<<5))) {
|
|
|
|
spiUnselect(driver);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ch2
|
|
|
|
// read (MSB=1) at location, and 1 word
|
|
|
|
spi_writew((0x8000 | 0x120 << 5) + 1);
|
|
|
|
|
|
|
|
if (!(recv_16bit_spi() & (1<<5))) {
|
|
|
|
spiUnselect(driver);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
spiUnselect(driver);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-25 18:35:21 -08:00
|
|
|
static void enable_flash() {
|
2019-06-06 17:12:03 -07:00
|
|
|
spiSelect(driver);
|
|
|
|
spi_writew(0x2001); //ch1
|
|
|
|
spi_writew(0x0018); //enable flash
|
|
|
|
spi_writew(0x2401); //ch2
|
|
|
|
spi_writew(0x0018); // enable flash
|
|
|
|
spiUnselect(driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void download_RAM(int target) {
|
|
|
|
unsigned short memory_area = 0; // memory area
|
|
|
|
unsigned short start_address = 0; // start address
|
|
|
|
unsigned short codeWidthRegAddr = 0; // code width register address
|
|
|
|
unsigned short size = 0; // size of RAM data
|
|
|
|
unsigned short command = 0; // command data
|
|
|
|
unsigned short data = 0; // RAM data
|
|
|
|
unsigned int k = 0; // used in loop for writing RAM data to the chip
|
|
|
|
const unsigned short *RAM_ptr; // pointer to array of data to be sent to the chip
|
|
|
|
|
|
|
|
|
|
|
|
//Why Again? For Every time, just in case?
|
|
|
|
setup_spi();
|
|
|
|
|
|
|
|
switch(target) // selects target
|
|
|
|
{
|
|
|
|
case CODE_RAM1:
|
|
|
|
memory_area = 0x1;
|
|
|
|
start_address = 0;
|
|
|
|
codeWidthRegAddr = 0x107;
|
|
|
|
RAM_ptr = MC33816_code_RAM1;
|
|
|
|
size = sizeof(MC33816_code_RAM1) / 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CODE_RAM2:
|
|
|
|
memory_area = 0x2;
|
|
|
|
start_address = 0;
|
|
|
|
codeWidthRegAddr = 0x127;
|
|
|
|
RAM_ptr = MC33816_code_RAM2;
|
|
|
|
size = sizeof(MC33816_code_RAM2) / 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DATA_RAM: // ch1 only?
|
|
|
|
memory_area = 0x4;
|
|
|
|
start_address = 0;
|
|
|
|
RAM_ptr = MC33816_data_RAM;
|
|
|
|
size = sizeof(MC33816_data_RAM) / 2;
|
|
|
|
break;
|
|
|
|
// optional, both data_rams with 0x3, writes same code to both
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Chip-Select high
|
|
|
|
spiSelect(driver);
|
|
|
|
|
|
|
|
if (target != DATA_RAM)
|
|
|
|
{
|
|
|
|
command = codeWidthRegAddr << 5; // control width register address
|
|
|
|
command |= 1; // number of words to follow
|
|
|
|
spi_writew(command); // sends code_width command
|
|
|
|
spi_writew(size); // sends size (Code Width)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select Channel command
|
|
|
|
spi_writew(0x7FE1);
|
|
|
|
// RAM1, RAM2, or Common Page (Data RAM)
|
|
|
|
spi_writew(memory_area);
|
|
|
|
|
|
|
|
// "Command" of starting address
|
|
|
|
// up to 0x03FE of code ram
|
|
|
|
// up to 0x0080 of data ram
|
|
|
|
command = start_address << 5;
|
|
|
|
spi_writew(command); // sends start address command
|
|
|
|
|
|
|
|
/*
|
|
|
|
for(k = 0; k < size; k++) // downloads RAM
|
|
|
|
{
|
|
|
|
data = *RAM_ptr; // retrieves data to be sent
|
|
|
|
spi_writew(data); // sends data
|
|
|
|
RAM_ptr++;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
spiSend(driver, size, RAM_ptr);
|
|
|
|
spiUnselect(driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void download_register(int r_target) {
|
|
|
|
unsigned short r_start_address = 0; // start address
|
|
|
|
unsigned short r_size = 0; // size of configuration data
|
|
|
|
unsigned short r_command = 0; // command data
|
|
|
|
unsigned short r_data = 0; // configuration data
|
|
|
|
int n = 0; // used for loop for writing data to the chip
|
|
|
|
unsigned short remainder_size = 0; // remainder size
|
|
|
|
const unsigned short *reg_ptr; // pointer to array of data to be sent to the chip
|
|
|
|
|
|
|
|
switch(r_target) // selects target
|
|
|
|
{
|
|
|
|
case REG_CH1: // channel 1 configurations
|
|
|
|
r_start_address = 0x100;
|
|
|
|
reg_ptr = MC33816_ch1_config;
|
|
|
|
r_size = sizeof(MC33816_ch1_config) / 2; // gets number of words to be sent
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REG_CH2: // channel 2 configurations
|
|
|
|
r_start_address = 0x120;
|
|
|
|
reg_ptr = MC33816_ch2_config;
|
|
|
|
r_size = sizeof(MC33816_ch2_config) / 2; // gets number of words to be sent
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REG_DIAG: // diagnostic configurations
|
|
|
|
r_start_address = 0x140;
|
|
|
|
reg_ptr = MC33816_diag_config;
|
|
|
|
r_size = sizeof(MC33816_diag_config) / 2; // gets number of words to be sent
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REG_IO: // IO configurations
|
|
|
|
r_start_address = 0x180;
|
|
|
|
reg_ptr = MC33816_io_config;
|
|
|
|
r_size = sizeof(MC33816_io_config) / 2; // gets number of words to be sent
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REG_MAIN: // main configurations
|
|
|
|
r_start_address = 0x1C0;
|
|
|
|
reg_ptr = MC33816_main_config;
|
|
|
|
r_size = sizeof(MC33816_main_config) / 2; // gets number of words to be sent
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//for location < size(remainder?)
|
|
|
|
// is location == 0? or past max xfer, send command + expected size
|
|
|
|
// if location = max xfer
|
|
|
|
//
|
|
|
|
// retrieve data, send it, increase pointer
|
|
|
|
// increase
|
|
|
|
|
2019-11-19 15:17:03 -08:00
|
|
|
if (r_size > MAX_SPI_MODE_A_TRANSFER_SIZE) //if size is too large, split into two sections ... MULTIPLE sections..
|
2019-06-06 17:12:03 -07:00
|
|
|
{
|
|
|
|
remainder_size = r_size - MAX_SPI_MODE_A_TRANSFER_SIZE; // creates remaining size
|
|
|
|
r_size = MAX_SPI_MODE_A_TRANSFER_SIZE; // sets first size
|
|
|
|
}
|
|
|
|
|
|
|
|
r_command = r_start_address << 5; // start address
|
|
|
|
r_command += r_size; // number of words to follow
|
|
|
|
|
|
|
|
spiSelect(driver); // Chip
|
|
|
|
|
|
|
|
spi_writew(r_command); // sends address and number of words to be sent
|
|
|
|
|
|
|
|
spiSend(driver, r_size, reg_ptr);
|
|
|
|
|
2019-11-19 15:17:03 -08:00
|
|
|
if (remainder_size > 0) // if remainder size is greater than 0, download the rest
|
2019-06-06 17:12:03 -07:00
|
|
|
{
|
|
|
|
r_start_address += r_size; // new start address
|
|
|
|
r_command = r_start_address << 5; // start address
|
|
|
|
r_command += remainder_size; // number of words to follow
|
|
|
|
|
|
|
|
spi_writew(r_command); // sends address and number of words to be sent
|
|
|
|
spiSend(driver, remainder_size, reg_ptr + r_size);
|
|
|
|
}
|
|
|
|
spiUnselect(driver);
|
2019-05-03 18:54:38 -07:00
|
|
|
}
|
|
|
|
|
2020-02-25 18:35:21 -08:00
|
|
|
void initMc33816(Logging *sharedLogger) {
|
|
|
|
logger = sharedLogger;
|
2020-02-25 19:38:08 -08:00
|
|
|
|
|
|
|
//
|
2020-04-12 15:03:53 -07:00
|
|
|
// see setTest33816EngineConfiguration for default configuration
|
2020-04-12 14:47:30 -07:00
|
|
|
|
2019-05-03 18:38:34 -07:00
|
|
|
if (CONFIG(mc33816_cs) == GPIO_UNASSIGNED)
|
|
|
|
return;
|
2020-04-12 15:03:53 -07:00
|
|
|
if (CONFIG(mc33816_rstb) == GPIO_UNASSIGNED)
|
|
|
|
return;
|
|
|
|
if (CONFIG(mc33816_driven) == GPIO_UNASSIGNED)
|
|
|
|
return;
|
2019-05-03 18:38:34 -07:00
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
// Initialize the chip via ResetB
|
|
|
|
resetB.initPin("mc33 RESTB", engineConfiguration->mc33816_rstb);
|
2020-04-12 15:03:53 -07:00
|
|
|
// High Voltage via DRIVEN
|
|
|
|
driven.initPin("mc33 DRIVEN", engineConfiguration->mc33816_driven);
|
|
|
|
driven.setValue(0); // ensure driven is off
|
2019-06-06 17:12:03 -07:00
|
|
|
|
2019-05-03 18:38:34 -07:00
|
|
|
chipSelect.initPin("mc33 CS", engineConfiguration->mc33816_cs /*, &engineConfiguration->csPinMode*/);
|
|
|
|
|
|
|
|
spiCfg.ssport = getHwPort("hip", CONFIG(mc33816_cs));
|
|
|
|
spiCfg.sspad = getHwPin("hip", CONFIG(mc33816_cs));
|
|
|
|
|
|
|
|
// hard-coded for now, just resolve the conflict with SD card!
|
|
|
|
engineConfiguration->mc33816spiDevice = SPI_DEVICE_3;
|
|
|
|
|
|
|
|
driver = getSpiDevice(engineConfiguration->mc33816spiDevice);
|
|
|
|
if (driver == NULL) {
|
|
|
|
// error already reported
|
2019-05-03 18:13:25 -07:00
|
|
|
return;
|
2019-05-03 18:38:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
spiStart(driver, &spiCfg);
|
2019-05-03 18:13:25 -07:00
|
|
|
|
2020-02-25 18:35:21 -08:00
|
|
|
addConsoleAction("mc33_stats", showStats);
|
2019-06-06 17:12:03 -07:00
|
|
|
//addConsoleActionI("mc33_send", sendWord);
|
|
|
|
|
|
|
|
// Does starting turn this high to begin with??
|
|
|
|
spiUnselect(driver);
|
2019-05-03 18:13:25 -07:00
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
//delay/wait? .. possibly only 30us for each needed, per datasheet
|
|
|
|
resetB.setValue(0);
|
|
|
|
chThdSleepMilliseconds(10);
|
|
|
|
resetB.setValue(1);
|
|
|
|
chThdSleepMilliseconds(10);
|
2020-04-13 16:01:22 -07:00
|
|
|
if (CONFIG(mc33816_flag0) != GPIO_UNASSIGNED) {
|
|
|
|
efiSetPadMode("mc33816 flag0", CONFIG(mc33816_flag0), getInputMode(PI_DEFAULT));
|
|
|
|
|
2020-04-13 16:14:30 -07:00
|
|
|
flag0before = efiReadPin(CONFIG(mc33816_flag0));
|
2020-04-13 16:01:22 -07:00
|
|
|
}
|
|
|
|
|
2019-05-03 18:54:38 -07:00
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
setup_spi();
|
2020-02-25 18:35:21 -08:00
|
|
|
mcChipId = readId();
|
2019-05-03 18:54:38 -07:00
|
|
|
|
2020-04-14 12:05:33 -07:00
|
|
|
if (!validateChipId()) {
|
|
|
|
firmwareError(OBD_PCM_Processor_Fault, "No comm with MC33");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
download_RAM(CODE_RAM1); // transfers code RAM1
|
|
|
|
download_RAM(CODE_RAM2); // transfers code RAM2
|
|
|
|
download_RAM(DATA_RAM); // transfers data RAM
|
2020-04-13 16:01:22 -07:00
|
|
|
/**
|
|
|
|
* current configuration of REG_MAIN would toggle flag0 from LOW to HIGH
|
|
|
|
*/
|
2019-06-06 17:12:03 -07:00
|
|
|
download_register(REG_MAIN); // download main register configurations
|
2020-04-13 16:01:22 -07:00
|
|
|
if (CONFIG(mc33816_flag0) != GPIO_UNASSIGNED) {
|
2020-04-13 16:14:30 -07:00
|
|
|
flag0after = efiReadPin(CONFIG(mc33816_flag0));
|
2020-04-13 17:01:06 -07:00
|
|
|
if (flag0before || !flag0after) {
|
2020-04-14 12:07:24 -07:00
|
|
|
firmwareError(OBD_PCM_Processor_Fault, "MC33 flag0 transition no buena");
|
2020-04-13 21:57:19 -07:00
|
|
|
return;
|
2020-04-13 17:01:06 -07:00
|
|
|
}
|
2020-04-13 16:01:22 -07:00
|
|
|
}
|
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
download_register(REG_CH1); // download channel 1 register configurations
|
|
|
|
download_register(REG_CH2); // download channel 2 register configurations
|
|
|
|
download_register(REG_IO); // download IO register configurations
|
|
|
|
download_register(REG_DIAG); // download diag register configuration
|
2020-04-13 21:57:19 -07:00
|
|
|
// Finished downloading, let's run the code
|
2019-06-06 17:12:03 -07:00
|
|
|
enable_flash();
|
2020-04-14 12:07:24 -07:00
|
|
|
if (!check_flash()) {
|
|
|
|
firmwareError(OBD_PCM_Processor_Fault, "MC33 flash validation failed");
|
2020-04-13 21:57:19 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-12 15:03:53 -07:00
|
|
|
driven.setValue(1); // driven = HV
|
2019-06-06 17:12:03 -07:00
|
|
|
}
|
2019-05-03 18:54:38 -07:00
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
void update_scv(unsigned short current)
|
|
|
|
{
|
|
|
|
// Update a single word in Data RAM
|
|
|
|
spiSelect(driver);
|
|
|
|
// Select Channel command
|
|
|
|
spi_writew(0x7FE1);
|
|
|
|
// Common Page
|
|
|
|
spi_writew(0x0004);
|
|
|
|
// write (MSB=0) at data ram x9 (SCV_I_Hold), and 1 word
|
|
|
|
spi_writew((9 << 5) + 1);
|
|
|
|
spi_writew(current);
|
|
|
|
spiUnselect(driver);
|
2019-05-03 18:54:38 -07:00
|
|
|
|
2019-06-06 17:12:03 -07:00
|
|
|
// Strobe it to reload the value
|
|
|
|
//GPIO_ClearPinsOutput(GPIOE, 1<<PIN21_IDX); // SCV
|
|
|
|
//GPIO_SetPinsOutput(GPIOE, 1<<PIN21_IDX); // SCV
|
2019-05-03 18:13:25 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* EFI_MC33816 */
|