mirror of https://github.com/rusefi/wideband.git
Merge branch 'master' of https://github.com/mck1117/wideband
This commit is contained in:
commit
c84a47a870
|
@ -0,0 +1,2 @@
|
|||
|
||||
*.sh text eol=lf
|
|
@ -1,4 +1,4 @@
|
|||
name: Build F0 Module Firmware
|
||||
name: Build Firmware
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
|
@ -6,6 +6,14 @@ jobs:
|
|||
build-firmware:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
# Let all builds finish even if one fails early
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- build-target: f0_module
|
||||
- build-target: f1_dave
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
|
@ -25,5 +33,11 @@ jobs:
|
|||
run: arm-none-eabi-gcc -v
|
||||
|
||||
- name: Build Firmware
|
||||
working-directory: ./firmware/boards/f0_module
|
||||
working-directory: ./firmware/boards/${{matrix.build-target}}
|
||||
run: ./build_wideband.sh
|
||||
|
||||
- name: Attach binaries
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Wideband ${{matrix.build-target}}
|
||||
path: ./firmware/build/wideband.*
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
[![Build Firmware](https://github.com/rusefi/wideband/actions/workflows/build-firmware.yaml/badge.svg)](https://github.com/rusefi/wideband/actions/workflows/build-firmware.yaml)
|
||||
|
||||
# rusEFI Wideband Controller
|
||||
|
||||
[User Documentation](https://rusefi.com/s/wb)
|
||||
|
||||
[Forum Thread](https://rusefi.com/forum/viewtopic.php?f=4&t=1856)
|
||||
|
||||
## Building Firmware
|
||||
|
||||
The `firmware/boards` directory contains configuration for each board this firmware supports.
|
||||
|
||||
For the standalone board and the module built in to rusEFI Hellen boards, `f0_module` is the correct target. Use the `build_wideband.sh` script to build and package the wideband firmware: both a bin including the bootloader, and a header file consumed by rusEFI that contains no bootloader, to be uploaded over CAN (via the aforementioned bootloader).
|
|
@ -131,6 +131,7 @@ CSRC = $(ALLCSRC) $(BOARDDIR)/board.c
|
|||
# setting.
|
||||
CPPSRC = $(ALLCPPSRC) \
|
||||
$(BOARDDIR)/port.cpp \
|
||||
$(BOARDDIR)/port_shared.cpp \
|
||||
shared/flash.cpp \
|
||||
can.cpp \
|
||||
can_helper.cpp \
|
||||
|
@ -152,7 +153,7 @@ ASMSRC = $(ALLASMSRC)
|
|||
ASMXSRC = $(ALLXASMSRC)
|
||||
|
||||
# Inclusion directories.
|
||||
INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) boards/
|
||||
INCDIR = $(CONFDIR) $(ALLINC) boards/ $(BOARDDIR)/io/
|
||||
|
||||
# Define C warning options here.
|
||||
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
|
||||
|
|
|
@ -119,7 +119,11 @@ CSRC = $(ALLCSRC) cfg/board.c
|
|||
|
||||
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
# setting.
|
||||
CPPSRC = $(ALLCPPSRC) bootloader.cpp $(SRCDIR)/shared/flash.cpp crc.cpp
|
||||
CPPSRC = $(ALLCPPSRC) \
|
||||
bootloader.cpp \
|
||||
../port_shared.cpp \
|
||||
$(SRCDIR)/shared/flash.cpp \
|
||||
$(SRCDIR)/shared/crc.cpp
|
||||
|
||||
# List ASM source files here.
|
||||
ASMSRC = $(ALLASMSRC)
|
||||
|
@ -128,7 +132,8 @@ ASMSRC = $(ALLASMSRC)
|
|||
ASMXSRC = $(ALLXASMSRC)
|
||||
|
||||
# Inclusion directories.
|
||||
INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) $(SRCDIR)/shared/
|
||||
INCDIR = $(CONFDIR) $(ALLINC) $(SRCDIR)/shared/ \
|
||||
../io ../..
|
||||
|
||||
# Define C warning options here.
|
||||
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include "port_shared.h"
|
||||
#include "flash.h"
|
||||
#include "io_pins.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
@ -36,15 +36,18 @@ void boot_app() {
|
|||
|
||||
const uint32_t* appFlash = __appflash_start__;
|
||||
|
||||
// The reset vector is at offset 4 (second uint32)
|
||||
uint32_t reset_vector = appFlash[1];
|
||||
|
||||
#ifdef STM32F0XX
|
||||
// copy vector table to sram
|
||||
// TODO: use __ram_vectors_size__
|
||||
memcpy(reinterpret_cast<char*>(&__ram_vectors_start__), appFlash, 256);
|
||||
|
||||
// The reset vector is at offset 4 (second uint32)
|
||||
uint32_t reset_vector = appFlash[1];
|
||||
|
||||
// M0 core version, newer cores do same thing a bit nicer
|
||||
// switch to use vectors in ram
|
||||
SYSCFG->CFGR1 |= 3;
|
||||
#endif
|
||||
|
||||
// TODO: is this necessary?
|
||||
//uint32_t app_msp = appLocation[0];
|
||||
|
@ -213,12 +216,6 @@ void RunBootloaderLoop()
|
|||
}
|
||||
}
|
||||
|
||||
static const CANConfig canConfig500 =
|
||||
{
|
||||
CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
|
||||
CAN_BTR_SJW(0) | CAN_BTR_BRP(5) | CAN_BTR_TS1(12) | CAN_BTR_TS2(1),
|
||||
};
|
||||
|
||||
THD_WORKING_AREA(waBootloaderThread, 512);
|
||||
THD_FUNCTION(BootloaderThread, arg)
|
||||
{
|
||||
|
@ -244,24 +241,23 @@ int main(void) {
|
|||
|
||||
chThdCreateStatic(waBootloaderThread, sizeof(waBootloaderThread), NORMALPRIO + 1, BootloaderThread, nullptr);
|
||||
|
||||
// PB5 is blue LED
|
||||
palSetPadMode(GPIOB, 5, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
// PB6 is green LED
|
||||
palSetPadMode(GPIOB, 6, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
palTogglePad(GPIOB, 6);
|
||||
palSetPadMode(LED_BLUE_PORT, LED_BLUE_PIN, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
|
||||
palSetPadMode(LED_GREEN_PORT, LED_GREEN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||
|
||||
for (size_t i = 0; i < 20; i++)
|
||||
{
|
||||
palTogglePad(GPIOB, 5);
|
||||
palTogglePad(GPIOB, 6);
|
||||
palTogglePad(LED_BLUE_PORT, LED_BLUE_PIN);
|
||||
palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||
chThdSleepMilliseconds(40);
|
||||
}
|
||||
|
||||
// Block until booting the app is allowed and CRC matches
|
||||
while (bootloaderBusy || !isAppValid())
|
||||
{
|
||||
palTogglePad(GPIOB, 5);
|
||||
palTogglePad(GPIOB, 6);
|
||||
palTogglePad(LED_BLUE_PORT, LED_BLUE_PIN);
|
||||
palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||
chThdSleepMilliseconds(200);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# first build the bootloader
|
||||
cd bootloader
|
||||
./build_bootloader.sh
|
||||
|
@ -8,11 +10,11 @@ cd bootloader
|
|||
cd ../../..
|
||||
|
||||
# delete the elf to force a re-link (it might not pick up the bootloader otherwise)
|
||||
rm -r build/
|
||||
rm ../for_rusefi/wideband_image.h
|
||||
rm -rf build/
|
||||
rm -f ../for_rusefi/wideband_image.h
|
||||
|
||||
# build app firmware!
|
||||
make -j12
|
||||
make -j12 BOARD=f0_module
|
||||
|
||||
# Copy the bin without the bootloader (the image consumed by rusEfi has no bootloader on it)
|
||||
dd if=build/wideband.bin of=build/wideband_noboot_no_pad.bin skip=6144 bs=1
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#define LED_BLUE_PORT GPIOB
|
||||
#define LED_BLUE_PIN 5
|
||||
|
||||
#define LED_GREEN_PORT GPIOB
|
||||
#define LED_GREEN_PIN 6
|
||||
|
||||
#define NERNST_ESR_DRIVER_PORT GPIOB
|
||||
#define NERNST_ESR_DRIVER_PIN 7
|
||||
|
||||
// PA7
|
||||
#define HEATER_PWM_DEVICE PWMD1
|
||||
#define HEATER_PWM_CHANNEL 0
|
||||
|
||||
// PA6
|
||||
#define PUMP_DAC_PWM_DEVICE PWMD3
|
||||
#define PUMP_DAC_PWM_CHANNEL 0
|
||||
|
||||
#define ID_SEL1_PORT GPIOB
|
||||
#define ID_SEL1_PIN 1
|
||||
|
||||
#define ID_SEL2_PORT GPIOA
|
||||
#define ID_SEL2_PIN 8
|
|
@ -97,8 +97,8 @@ Configuration GetConfiguration()
|
|||
}
|
||||
|
||||
// Now, override the index with a hardware-strapped option (if present)
|
||||
auto sel1 = readSelPin(GPIOB, 1);
|
||||
auto sel2 = readSelPin(GPIOA, 8);
|
||||
auto sel1 = readSelPin(ID_SEL1_PORT, ID_SEL1_PIN);
|
||||
auto sel2 = readSelPin(ID_SEL2_PORT, ID_SEL2_PIN);
|
||||
|
||||
// See https://github.com/mck1117/wideband/issues/11
|
||||
switch (3 * sel1 + sel2) {
|
||||
|
@ -129,9 +129,3 @@ void SetConfiguration(const Configuration& newConfig)
|
|||
sizeof(Configuration)
|
||||
);
|
||||
}
|
||||
|
||||
const CANConfig canConfig500 =
|
||||
{
|
||||
CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
|
||||
CAN_BTR_SJW(0) | CAN_BTR_BRP(5) | CAN_BTR_TS1(12) | CAN_BTR_TS2(1),
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#include "port_shared.h"
|
||||
|
||||
// board-specific stuff shared between bootloader and firmware
|
||||
|
||||
const CANConfig canConfig500 =
|
||||
{
|
||||
CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
|
||||
/*
|
||||
For 48MHz http://www.bittiming.can-wiki.info/ gives us Pre-scaler=6, Seq 1=13 and Seq 2=2. Subtract '1' for register values
|
||||
*/
|
||||
CAN_BTR_SJW(0) | CAN_BTR_BRP(5) | CAN_BTR_TS1(12) | CAN_BTR_TS2(1),
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
// Fundamental board constants
|
||||
#define VCC_VOLTS (3.3f)
|
||||
#define HALF_VCC (VCC_VOLTS / 2)
|
||||
#define ADC_MAX_COUNT (4095)
|
||||
#define ADC_OVERSAMPLE 24
|
||||
|
||||
// *******************************
|
||||
// Nernst voltage & ESR sense
|
||||
// *******************************
|
||||
#define NERNST_INPUT_GAIN (1 / 2.7f)
|
|
@ -17,30 +17,26 @@
|
|||
#ifndef _BOARD_H_
|
||||
#define _BOARD_H_
|
||||
|
||||
/*
|
||||
* Setup for the ST INEMO-M1 Discovery board.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Board identifier.
|
||||
*/
|
||||
#define BOARD_ST_NUCLEO64_F103RB
|
||||
#define BOARD_NAME "STMicroelectronics NUCLEO-F103RB"
|
||||
/*
|
||||
* Board identifier.
|
||||
*/
|
||||
#define BOARD_NAME "rusEfi Wideband Controller"
|
||||
|
||||
/*
|
||||
* Board frequencies.
|
||||
* Board oscillators-related settings.
|
||||
* NOTE: LSE not fitted.
|
||||
* NOTE: HSE not fitted.
|
||||
*/
|
||||
#define STM32_LSECLK 0
|
||||
#if !defined(STM32_LSECLK)
|
||||
#define STM32_LSECLK 0U
|
||||
#endif
|
||||
|
||||
#if defined(NUCLEO_EXTERNAL_OSCILLATOR)
|
||||
#define STM32_HSECLK 8000000
|
||||
#define STM32_HSE_BYPASS
|
||||
|
||||
#elif defined(NUCLEO_HSE_CRYSTAL)
|
||||
#define STM32_HSECLK 8000000
|
||||
|
||||
#else
|
||||
#define STM32_HSECLK 0
|
||||
#if !defined(STM32_HSECLK)
|
||||
#define STM32_HSECLK 0U
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -191,20 +187,28 @@
|
|||
/*
|
||||
* Port A setup.
|
||||
* Everything input with pull-up except:
|
||||
* PA2 - Alternate output (GPIOA_ARD_D1, GPIOA_USART2_TX).
|
||||
* PA3 - Normal input (GPIOA_ARD_D0, GPIOA_USART2_RX).
|
||||
* PA5 - Push Pull output (GPIOA_LED_GREEN).
|
||||
* PA0 - Ip_sense (analog in).
|
||||
* PA1 - Ip_dac (PWM) (output pushpull, alternate).
|
||||
* PA2 - Vm_sense (analog in).
|
||||
* PA3 - Un_sense (analog in).
|
||||
* PA4 - Vm (analog in, unused).
|
||||
* PA8 - Green LED (output pushpull).
|
||||
* PA9 - UART TX (output pushpull, alternate).
|
||||
* PA10 - UART RX (digital input, alternate).
|
||||
* PA11 - CAN RX (digital input, alternate).
|
||||
* PA12 - CAN TX (output pushpull, alternate).
|
||||
* PA13 - Pull-up input (GPIOA_SWDIO).
|
||||
* PA14 - Pull-down input (GPIOA_SWCLK).
|
||||
*/
|
||||
#define VAL_GPIOACRL 0x88384B88 /* PA7...PA0 */
|
||||
#define VAL_GPIOACRH 0x88888888 /* PA15...PA8 */
|
||||
#define VAL_GPIOAODR 0xFFFFBFDF
|
||||
#define VAL_GPIOACRL 0x88800020 /* PA7...PA0 */
|
||||
#define VAL_GPIOACRH 0x888B88B2 /* PA15...PA8 */
|
||||
#define VAL_GPIOAODR 0xFFFFFFFF
|
||||
|
||||
/*
|
||||
* Port B setup.
|
||||
* Everything input with pull-up except:
|
||||
* PB3 - Pull-up input (GPIOA_SWO).
|
||||
* PB12 - Nernst ESR driver (GPIO) (output pushpull)
|
||||
* PB13 - Blue LED (output pushpull)
|
||||
*/
|
||||
#define VAL_GPIOBCRL 0x88888888 /* PB7...PB0 */
|
||||
#define VAL_GPIOBCRH 0x88888888 /* PB15...PB8 */
|
||||
|
@ -213,19 +217,16 @@
|
|||
/*
|
||||
* Port C setup.
|
||||
* Everything input with pull-up except:
|
||||
* PC13 - Normal input (GPIOC_BUTTON).
|
||||
*/
|
||||
#define VAL_GPIOCCRL 0x88888888 /* PC7...PC0 */
|
||||
#define VAL_GPIOCCRH 0x88488888 /* PC15...PC8 */
|
||||
#define VAL_GPIOCCRH 0x88888888 /* PC15...PC8 */
|
||||
#define VAL_GPIOCODR 0xFFFFFFFF
|
||||
|
||||
/*
|
||||
* Port D setup.
|
||||
* Everything input with pull-up except:
|
||||
* PD0 - Normal input (GPIOD_OSC_IN).
|
||||
* PD1 - Normal input (GPIOD_OSC_OUT).
|
||||
*/
|
||||
#define VAL_GPIODCRL 0x88888844 /* PD7...PD0 */
|
||||
#define VAL_GPIODCRL 0x88888888 /* PD7...PD0 */
|
||||
#define VAL_GPIODCRH 0x88888888 /* PD15...PD8 */
|
||||
#define VAL_GPIODODR 0xFFFFFFFF
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
USE_BOOTLOADER = no
|
||||
|
||||
MCU = cortex-m3
|
||||
|
||||
include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f1xx.mk
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# back out to the root
|
||||
cd ../..
|
||||
|
||||
# build app firmware!
|
||||
make -j12 BOARD=f1_dave ECHO_UART=TRUE
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#define LED_BLUE_PORT GPIOB
|
||||
#define LED_BLUE_PIN 13
|
||||
|
||||
#define LED_GREEN_PORT GPIOA
|
||||
#define LED_GREEN_PIN 8
|
||||
|
||||
#define NERNST_ESR_DRIVER_PORT GPIOB
|
||||
#define NERNST_ESR_DRIVER_PIN 12
|
||||
|
||||
// PC13
|
||||
#define HEATER_PWM_DEVICE PWMD1
|
||||
#define HEATER_PWM_CHANNEL 0
|
||||
|
||||
// PA1 TIM2_CH2
|
||||
#define PUMP_DAC_PWM_DEVICE PWMD2
|
||||
#define PUMP_DAC_PWM_CHANNEL 1
|
||||
|
||||
#define ID_SEL1_PORT GPIOB
|
||||
#define ID_SEL1_PIN 0
|
||||
|
||||
#define ID_SEL2_PORT GPIOB
|
||||
#define ID_SEL2_PIN 1
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
/*
|
||||
* HAL driver system settings.
|
||||
* TL,DR: we run at 48MHz.
|
||||
* It's not possible to run at 72 on HSI because of the PLL's limited configuration options, so 48MHz right now.
|
||||
*/
|
||||
#define STM32_NO_INIT FALSE
|
||||
#define STM32_HSI_ENABLED TRUE
|
||||
|
@ -47,7 +49,7 @@
|
|||
#define STM32_PLLMUL_VALUE 12
|
||||
#define STM32_HPRE STM32_HPRE_DIV1
|
||||
#define STM32_PPRE1 STM32_PPRE1_DIV2
|
||||
#define STM32_PPRE2 STM32_PPRE2_DIV2
|
||||
#define STM32_PPRE2 STM32_PPRE2_DIV1
|
||||
#define STM32_ADCPRE STM32_ADCPRE_DIV4
|
||||
#define STM32_USB_CLOCK_REQUIRED TRUE
|
||||
#define STM32_USBPRE STM32_USBPRE_DIV1
|
||||
|
@ -133,8 +135,8 @@
|
|||
*/
|
||||
#define STM32_PWM_USE_ADVANCED FALSE
|
||||
#define STM32_PWM_USE_TIM1 TRUE
|
||||
#define STM32_PWM_USE_TIM2 FALSE
|
||||
#define STM32_PWM_USE_TIM3 TRUE
|
||||
#define STM32_PWM_USE_TIM2 TRUE
|
||||
#define STM32_PWM_USE_TIM3 FALSE
|
||||
#define STM32_PWM_USE_TIM4 FALSE
|
||||
#define STM32_PWM_USE_TIM5 FALSE
|
||||
#define STM32_PWM_USE_TIM8 FALSE
|
||||
|
@ -182,7 +184,7 @@
|
|||
* ST driver system settings.
|
||||
*/
|
||||
#define STM32_ST_IRQ_PRIORITY 8
|
||||
#define STM32_ST_USE_TIMER 2
|
||||
#define STM32_ST_USE_TIMER 3
|
||||
|
||||
/*
|
||||
* UART driver system settings.
|
||||
|
|
|
@ -1,16 +1,58 @@
|
|||
#include "port.h"
|
||||
|
||||
|
||||
#include "wideband_config.h"
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
#define ADC_CHANNEL_COUNT 3
|
||||
#define ADC_SAMPLE ADC_SAMPLE_7P5
|
||||
|
||||
static adcsample_t adcBuffer[ADC_CHANNEL_COUNT * ADC_OVERSAMPLE];
|
||||
|
||||
ADCConversionGroup convGroup =
|
||||
{
|
||||
false,
|
||||
ADC_CHANNEL_COUNT,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0, ADC_CR2_CONT, // CR1, CR2
|
||||
// SMPR1
|
||||
0,
|
||||
// SMPR2
|
||||
ADC_SMPR2_SMP_AN3(ADC_SAMPLE) | ADC_SMPR2_SMP_AN0(ADC_SAMPLE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLE),
|
||||
// SQR
|
||||
ADC_SQR1_NUM_CH(ADC_CHANNEL_COUNT),
|
||||
0,
|
||||
ADC_SQR3_SQ1_N(3) | ADC_SQR3_SQ2_N(0) | ADC_SQR3_SQ3_N(2)
|
||||
};
|
||||
|
||||
static float AverageSamples(adcsample_t* buffer, size_t idx)
|
||||
{
|
||||
uint32_t sum = 0;
|
||||
|
||||
for (size_t i = 0; i < ADC_OVERSAMPLE; i++)
|
||||
{
|
||||
sum += buffer[idx];
|
||||
idx += ADC_CHANNEL_COUNT;
|
||||
}
|
||||
|
||||
constexpr float scale = VCC_VOLTS / (ADC_MAX_COUNT * ADC_OVERSAMPLE);
|
||||
|
||||
return (float)sum * scale;
|
||||
}
|
||||
|
||||
AnalogResult AnalogSample()
|
||||
{
|
||||
// TODO: implement me!
|
||||
return {};
|
||||
}
|
||||
adcConvert(&ADCD1, &convGroup, adcBuffer, ADC_OVERSAMPLE);
|
||||
|
||||
const CANConfig canConfig500 =
|
||||
return
|
||||
{
|
||||
CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
|
||||
0 // TODO: set bit timing! correctly!
|
||||
.NernstVoltage = AverageSamples(adcBuffer, 0) * NERNST_INPUT_GAIN,
|
||||
.PumpCurrentVoltage = AverageSamples(adcBuffer, 1),
|
||||
.VirtualGroundVoltageInt = AverageSamples(adcBuffer, 2),
|
||||
};
|
||||
}
|
||||
|
||||
Configuration GetConfiguration()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#include "port_shared.h"
|
||||
|
||||
// board-specific stuff shared between bootloader and firmware
|
||||
|
||||
const CANConfig canConfig500 =
|
||||
{
|
||||
CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
|
||||
/*
|
||||
For 24MHz http://www.bittiming.can-wiki.info/ gives us Pre-scaler=3, Seq 1=13 and Seq 2=2. Subtract '1' for register values
|
||||
*/
|
||||
CAN_BTR_SJW(0) | CAN_BTR_BRP(2) | CAN_BTR_TS1(12) | CAN_BTR_TS2(1),
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
// Fundamental board constants
|
||||
#define VCC_VOLTS (3.3f)
|
||||
#define HALF_VCC (VCC_VOLTS / 2)
|
||||
#define ADC_MAX_COUNT (4095)
|
||||
#define ADC_OVERSAMPLE 24
|
||||
|
||||
// *******************************
|
||||
// Nernst voltage & ESR sense
|
||||
// *******************************
|
||||
#define NERNST_INPUT_GAIN (1 / 3.0f)
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "hal.h"
|
||||
#include "port_shared.h"
|
||||
|
||||
struct AnalogResult
|
||||
{
|
||||
|
@ -33,4 +34,3 @@ public:
|
|||
Configuration GetConfiguration();
|
||||
void SetConfiguration(const Configuration& newConfig);
|
||||
|
||||
extern const CANConfig canConfig500;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
extern const CANConfig canConfig500;
|
|
@ -1,6 +1,7 @@
|
|||
#include "can.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include "fault.h"
|
||||
#include "can_helper.h"
|
||||
#include "heater_control.h"
|
||||
#include "lambda_conversion.h"
|
||||
|
@ -8,6 +9,9 @@
|
|||
#include "pump_dac.h"
|
||||
#include "port.h"
|
||||
|
||||
// this same header is imported by rusEFI to get struct layouts and firmware version
|
||||
#include "../for_rusefi/wideband_can.h"
|
||||
|
||||
Configuration configuration;
|
||||
|
||||
static THD_WORKING_AREA(waCanTxThread, 256);
|
||||
|
@ -15,7 +19,7 @@ void CanTxThread(void*)
|
|||
{
|
||||
while(1)
|
||||
{
|
||||
SendEmulatedAemXseries(configuration.CanIndexOffset);
|
||||
SendRusefiFormat(configuration.CanIndexOffset);
|
||||
|
||||
chThdSleepMilliseconds(10);
|
||||
}
|
||||
|
@ -26,7 +30,7 @@ static void SendAck()
|
|||
CANTxFrame frame;
|
||||
|
||||
frame.IDE = CAN_IDE_EXT;
|
||||
frame.EID = 0x727573; // ascii "rus"
|
||||
frame.EID = WB_ACK;
|
||||
frame.RTR = CAN_RTR_DATA;
|
||||
frame.DLC = 0;
|
||||
|
||||
|
@ -53,7 +57,7 @@ void CanRxThread(void*)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (frame.DLC == 2 && frame.EID == 0xEF5'0000) {
|
||||
if (frame.DLC == 2 && frame.EID == WB_MGS_ECU_STATUS) {
|
||||
// This is status from ECU - battery voltage and heater enable signal
|
||||
|
||||
// data0 contains battery voltage in tenths of a volt
|
||||
|
@ -65,7 +69,7 @@ void CanRxThread(void*)
|
|||
SetHeaterAllowed(heaterAllowed);
|
||||
}
|
||||
// If it's a bootloader entry request, reboot to the bootloader!
|
||||
else if (frame.DLC == 0 && frame.EID == 0xEF0'0000)
|
||||
else if (frame.DLC == 0 && frame.EID == WB_BL_ENTER)
|
||||
{
|
||||
SendAck();
|
||||
|
||||
|
@ -75,7 +79,7 @@ void CanRxThread(void*)
|
|||
NVIC_SystemReset();
|
||||
}
|
||||
// Check if it's an "index set" message
|
||||
else if (frame.DLC == 1 && frame.EID == 0xEF4'0000)
|
||||
else if (frame.DLC == 1 && frame.EID == WB_MSG_SET_INDEX)
|
||||
{
|
||||
auto newCfg = GetConfiguration();
|
||||
newCfg.CanIndexOffset = frame.data8[0];
|
||||
|
@ -95,61 +99,35 @@ void InitCan()
|
|||
chThdCreateStatic(waCanRxThread, sizeof(waCanRxThread), NORMALPRIO - 4, CanRxThread, nullptr);
|
||||
}
|
||||
|
||||
struct StandardDataFrame
|
||||
{
|
||||
uint16_t lambda;
|
||||
uint16_t measuredResistance;
|
||||
uint8_t pad[4];
|
||||
};
|
||||
|
||||
#define SWAP_UINT16(x) (((x) << 8) | ((x) >> 8))
|
||||
|
||||
void SendEmulatedAemXseries(uint8_t idx) {
|
||||
CanTxMessage frame(0x180 + idx, 8, true);
|
||||
|
||||
bool isValid = IsRunningClosedLoop();
|
||||
|
||||
float lambda = GetLambda();
|
||||
uint16_t intLambda = lambda * 10000;
|
||||
|
||||
// swap endian
|
||||
intLambda = SWAP_UINT16(intLambda);
|
||||
*reinterpret_cast<uint16_t*>(&frame[0]) = intLambda;
|
||||
|
||||
// bit 1 = LSU 4.9 detected
|
||||
// bit 7 = reading valid
|
||||
frame[6] = 0x02 | (isValid ? 0x80 : 0x00);
|
||||
|
||||
// Hijack a reserved bit to indicate that we're NOT an AEM controller
|
||||
frame[7] = 0x80;
|
||||
|
||||
// Now we embed some extra data for debug
|
||||
// bytes 2-3 are officially oxygen percent
|
||||
// byte 4 is officially supply voltage
|
||||
|
||||
// Report pump output PWM in byte 2, 0-255 for min to max target (128 = 0 current)
|
||||
frame[2] = GetPumpOutputDuty() / 4;
|
||||
|
||||
// Report sensor ESR in byte 3, 4 ohm steps
|
||||
int esrVal = (int)GetSensorInternalResistance() / 4;
|
||||
|
||||
// Clamp to uint8_t limits
|
||||
if (esrVal > 255) {
|
||||
esrVal = 255;
|
||||
} else if (esrVal < 0) {
|
||||
esrVal = 0;
|
||||
}
|
||||
|
||||
frame[3] = esrVal;
|
||||
|
||||
// Report current nernst voltage in byte 4, 5mv steps
|
||||
frame[4] = (int)(GetNernstDc() * 200);
|
||||
}
|
||||
|
||||
void SendCanData(float lambda, uint16_t measuredResistance)
|
||||
void SendRusefiFormat(uint8_t idx)
|
||||
{
|
||||
CanTxTyped<StandardDataFrame> frame(0x130);
|
||||
auto baseAddress = 0x190 + 2 * idx;
|
||||
auto esr = GetSensorInternalResistance();
|
||||
|
||||
frame.get().lambda = lambda * 10000;
|
||||
frame.get().measuredResistance = measuredResistance;
|
||||
{
|
||||
CanTxTyped<wbo::StandardData> frame(baseAddress + 0);
|
||||
|
||||
// The same header is imported by the ECU and checked against this data in the frame
|
||||
frame.get().Version = RUSEFI_WIDEBAND_VERSION;
|
||||
|
||||
uint16_t lambda = GetLambda() * 10000;
|
||||
frame.get().Lambda = lambda;
|
||||
|
||||
// TODO: decode temperature instead of putting ESR here
|
||||
frame.get().TemperatureC = esr;
|
||||
|
||||
frame.get().Valid = IsRunningClosedLoop() ? 0x01 : 0x00;
|
||||
}
|
||||
|
||||
{
|
||||
CanTxTyped<wbo::DiagData> frame(baseAddress + 1);
|
||||
|
||||
frame.get().Esr = esr;
|
||||
frame.get().NernstDc = GetNernstDc() * 1000;
|
||||
frame.get().PumpDuty = GetPumpOutputDuty() * 255;
|
||||
frame.get().Status = GetCurrentFault();
|
||||
frame.get().HeaterDuty = GetHeaterDuty() * 255;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
void InitCan();
|
||||
void SendCanData(float lambda, uint16_t measuredResistance);
|
||||
void SendEmulatedAemXseries(uint8_t idx);
|
||||
void SendRusefiFormat(uint8_t idx);
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
#include "fault.h"
|
||||
|
||||
using namespace wbo;
|
||||
|
||||
static Fault currentFault = Fault::None;
|
||||
|
||||
void setFault(Fault fault)
|
||||
void SetFault(Fault fault)
|
||||
{
|
||||
currentFault = fault;
|
||||
}
|
||||
|
||||
bool hasFault()
|
||||
bool HasFault()
|
||||
{
|
||||
return currentFault != Fault::None;
|
||||
}
|
||||
|
||||
Fault getCurrentFault()
|
||||
Fault GetCurrentFault()
|
||||
{
|
||||
return currentFault;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
enum class Fault
|
||||
{
|
||||
None = 0,
|
||||
#include <cstdint>
|
||||
|
||||
// First fault code at 3 so it's easier to see
|
||||
SensorDidntHeat = 3,
|
||||
SensorOverheat = 4,
|
||||
SensorUnderheat = 5,
|
||||
};
|
||||
#include "../for_rusefi/wideband_can.h"
|
||||
|
||||
void setFault(Fault fault);
|
||||
bool hasFault();
|
||||
Fault getCurrentFault();
|
||||
void SetFault(wbo::Fault fault);
|
||||
bool HasFault();
|
||||
wbo::Fault GetCurrentFault();
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
#include "sampling.h"
|
||||
#include "pid.h"
|
||||
|
||||
using namespace wbo;
|
||||
|
||||
// 400khz / 1024 = 390hz PWM
|
||||
Pwm heaterPwm(PWMD1, 0, 400'000, 1024);
|
||||
static Pwm heaterPwm(HEATER_PWM_DEVICE, HEATER_PWM_CHANNEL, 400'000, 1024);
|
||||
|
||||
enum class HeaterState
|
||||
{
|
||||
|
@ -61,7 +63,7 @@ static HeaterState GetNextState(HeaterState state, float sensorEsr)
|
|||
}
|
||||
else if (timeCounter == 0)
|
||||
{
|
||||
setFault(Fault::SensorDidntHeat);
|
||||
SetFault(Fault::SensorDidntHeat);
|
||||
return HeaterState::Stopped;
|
||||
}
|
||||
|
||||
|
@ -69,14 +71,15 @@ static HeaterState GetNextState(HeaterState state, float sensorEsr)
|
|||
|
||||
break;
|
||||
case HeaterState::ClosedLoop:
|
||||
// Check that the sensor's ESR is acceptable for normal operation
|
||||
if (sensorEsr < HEATER_OVERHEAT_ESR)
|
||||
{
|
||||
setFault(Fault::SensorOverheat);
|
||||
SetFault(Fault::SensorOverheat);
|
||||
return HeaterState::Stopped;
|
||||
}
|
||||
else if (sensorEsr > HEATER_UNDERHEAT_ESR)
|
||||
{
|
||||
setFault(Fault::SensorUnderheat);
|
||||
SetFault(Fault::SensorUnderheat);
|
||||
return HeaterState::Stopped;
|
||||
}
|
||||
|
||||
|
@ -201,3 +204,8 @@ void SetHeaterAllowed(bool allowed)
|
|||
{
|
||||
heaterAllowed = allowed;
|
||||
}
|
||||
|
||||
float GetHeaterDuty()
|
||||
{
|
||||
return heaterPwm.GetLastDuty();
|
||||
}
|
||||
|
|
|
@ -6,3 +6,4 @@ void StartHeaterControl();
|
|||
bool IsRunningClosedLoop();
|
||||
void SetBatteryVoltage(float vbatt);
|
||||
void SetHeaterAllowed(bool allowed);
|
||||
float GetHeaterDuty();
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
#include "pump_dac.h"
|
||||
#include "sampling.h"
|
||||
#include "uart.h"
|
||||
#include "io_pins.h"
|
||||
|
||||
using namespace wbo;
|
||||
|
||||
/*
|
||||
* Application entry point.
|
||||
|
@ -24,19 +26,21 @@ int main() {
|
|||
StartPumpControl();
|
||||
|
||||
InitCan();
|
||||
//InitUart();
|
||||
#ifdef ECHO_UART
|
||||
InitUart();
|
||||
#endif
|
||||
|
||||
while(true)
|
||||
{
|
||||
auto fault = getCurrentFault();
|
||||
auto fault = GetCurrentFault();
|
||||
|
||||
if (fault == Fault::None)
|
||||
{
|
||||
// blue is off
|
||||
palClearPad(GPIOB, 5);
|
||||
palClearPad(LED_BLUE_PORT, LED_BLUE_PIN);
|
||||
|
||||
// Green is blinking
|
||||
palTogglePad(GPIOB, 6);
|
||||
palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||
|
||||
// Slow blink if closed loop, fast if not
|
||||
chThdSleepMilliseconds(IsRunningClosedLoop() ? 700 : 50);
|
||||
|
@ -44,13 +48,13 @@ int main() {
|
|||
else
|
||||
{
|
||||
// green is off
|
||||
palClearPad(GPIOB, 6);
|
||||
palClearPad(LED_GREEN_PORT, LED_GREEN_PIN);
|
||||
|
||||
// Blink out the error code
|
||||
for (int i = 0; i < 2 * static_cast<int>(fault); i++)
|
||||
{
|
||||
// Blue is blinking
|
||||
palTogglePad(GPIOB, 5);
|
||||
palTogglePad(LED_BLUE_PORT, LED_BLUE_PIN);
|
||||
|
||||
// fast blink
|
||||
chThdSleepMilliseconds(300);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "hal.h"
|
||||
|
||||
// 48MHz / 1024 = 46.8khz PWM
|
||||
static Pwm pumpDac(PWMD3, 0, 48'000'000, 1024);
|
||||
static Pwm pumpDac(PUMP_DAC_PWM_DEVICE, PUMP_DAC_PWM_CHANNEL, 48'000'000, 1024);
|
||||
|
||||
void InitPumpDac()
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ void SetPumpCurrentTarget(int32_t microampere)
|
|||
pumpDac.SetDuty(volts / VCC_VOLTS);
|
||||
}
|
||||
|
||||
uint16_t GetPumpOutputDuty()
|
||||
float GetPumpOutputDuty()
|
||||
{
|
||||
return pumpDac.GetLastDuty();
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
void InitPumpDac();
|
||||
void SetPumpCurrentTarget(int32_t microamperes);
|
||||
uint16_t GetPumpOutputDuty();
|
||||
float GetPumpOutputDuty();
|
||||
|
|
|
@ -45,14 +45,14 @@ float clampF(float min, float clamp, float max) {
|
|||
}
|
||||
|
||||
void Pwm::SetDuty(float duty) {
|
||||
pwmcnt_t highTime = m_counterPeriod * clampF(0, duty, 1);
|
||||
|
||||
m_lastDuty = highTime;
|
||||
auto dutyFloat = clampF(0, duty, 1);
|
||||
m_dutyFloat = dutyFloat;
|
||||
pwmcnt_t highTime = m_counterPeriod * dutyFloat;
|
||||
|
||||
pwm_lld_enable_channel(m_driver, m_channel, highTime);
|
||||
}
|
||||
|
||||
uint16_t Pwm::GetLastDuty() const
|
||||
float Pwm::GetLastDuty() const
|
||||
{
|
||||
return m_lastDuty;
|
||||
return m_dutyFloat;
|
||||
}
|
||||
|
|
|
@ -11,11 +11,12 @@ public:
|
|||
|
||||
void Start();
|
||||
void SetDuty(float duty);
|
||||
uint16_t GetLastDuty() const;
|
||||
float GetLastDuty() const;
|
||||
|
||||
private:
|
||||
PWMDriver* const m_driver;
|
||||
const uint8_t m_channel;
|
||||
const uint32_t m_counterFrequency;
|
||||
const uint16_t m_counterPeriod;
|
||||
uint16_t m_lastDuty;
|
||||
float m_dutyFloat;
|
||||
};
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
#include "wideband_config.h"
|
||||
|
||||
#include "port.h"
|
||||
#include "io_pins.h"
|
||||
|
||||
// Stored results
|
||||
float nernstAc = 0;
|
||||
float nernstDc = 0;
|
||||
volatile float pumpCurrentSenseVoltage = 0;
|
||||
static float nernstAc = 0;
|
||||
static float nernstDc = 0;
|
||||
static volatile float pumpCurrentSenseVoltage = 0;
|
||||
|
||||
constexpr float f_abs(float x)
|
||||
{
|
||||
|
@ -29,7 +30,7 @@ static void SamplingThread(void*)
|
|||
auto result = AnalogSample();
|
||||
|
||||
// Toggle the pin after sampling so that any switching noise occurs while we're doing our math instead of when sampling
|
||||
palTogglePad(GPIOB, 7);
|
||||
palTogglePad(NERNST_ESR_DRIVER_PORT, NERNST_ESR_DRIVER_PIN);
|
||||
|
||||
float r_1 = result.NernstVoltage;
|
||||
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
// Fundamental board constants
|
||||
#define VCC_VOLTS (3.3f)
|
||||
#define HALF_VCC (VCC_VOLTS / 2)
|
||||
#define ADC_MAX_COUNT (4095)
|
||||
#define ADC_OVERSAMPLE 24
|
||||
#include "io_pins.h"
|
||||
#include "wideband_board_config.h"
|
||||
|
||||
// *******************************
|
||||
// Nernst voltage & ESR sense
|
||||
// *******************************
|
||||
#define NERNST_INPUT_GAIN (1 / 2.7f)
|
||||
|
||||
// Nernst AC injection resistor value
|
||||
#define ESR_SUPPLY_R (22000)
|
||||
|
@ -42,9 +38,9 @@
|
|||
#define HEATER_CONTROL_PERIOD 50
|
||||
|
||||
#define HEATER_PREHEAT_TIME 5000
|
||||
#define HEATER_WARMUP_TIMEOUT 45000
|
||||
#define HEATER_WARMUP_TIMEOUT 60000
|
||||
|
||||
#define HEATER_CLOSED_LOOP_THRESHOLD_ESR 400
|
||||
#define HEATER_CLOSED_LOOP_THRESHOLD_ESR 500
|
||||
#define HEATER_TARGET_ESR 300
|
||||
#define HEATER_OVERHEAT_ESR 150
|
||||
#define HEATER_UNDERHEAT_ESR 600
|
||||
#define HEATER_UNDERHEAT_ESR 700
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#define RUSEFI_WIDEBAND_VERSION (0xA0)
|
||||
|
||||
// ascii "rus"
|
||||
#define WB_ACK 0x727573
|
||||
#define WB_BL_ENTER 0xEF0'0000
|
||||
#define WB_MSG_SET_INDEX 0xEF4'0000
|
||||
#define WB_MGS_ECU_STATUS 0xEF5'0000
|
||||
|
||||
namespace wbo
|
||||
{
|
||||
enum class Fault : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
|
||||
// First fault code at 3 so it's easier to see
|
||||
SensorDidntHeat = 3,
|
||||
SensorOverheat = 4,
|
||||
SensorUnderheat = 5,
|
||||
};
|
||||
|
||||
struct StandardData
|
||||
{
|
||||
uint8_t Version;
|
||||
uint8_t Valid;
|
||||
|
||||
uint16_t Lambda;
|
||||
uint16_t TemperatureC;
|
||||
|
||||
uint16_t pad;
|
||||
};
|
||||
|
||||
struct DiagData
|
||||
{
|
||||
uint16_t Esr;
|
||||
uint16_t NernstDc;
|
||||
uint8_t PumpDuty;
|
||||
Fault Status;
|
||||
|
||||
uint8_t HeaterDuty;
|
||||
uint8_t pad;
|
||||
};
|
||||
|
||||
static const char* describeFault(Fault fault) {
|
||||
switch (fault) {
|
||||
case Fault::None:
|
||||
return "OK";
|
||||
case Fault::SensorDidntHeat:
|
||||
return "Sensor failed to heat";
|
||||
case Fault::SensorOverheat:
|
||||
return "Sensor overheat";
|
||||
case Fault::SensorUnderheat:
|
||||
return "Sensor underheat";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace wbo
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Loading…
Reference in New Issue