This commit is contained in:
Matthew Kennedy 2022-04-04 14:21:32 -07:00
commit c84a47a870
38 changed files with 907 additions and 679 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
*.sh text eol=lf

View File

@ -1,4 +1,4 @@
name: Build F0 Module Firmware name: Build Firmware
on: [push, pull_request] on: [push, pull_request]
@ -6,6 +6,14 @@ jobs:
build-firmware: build-firmware:
runs-on: ubuntu-latest 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: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
with: with:
@ -25,5 +33,11 @@ jobs:
run: arm-none-eabi-gcc -v run: arm-none-eabi-gcc -v
- name: Build Firmware - name: Build Firmware
working-directory: ./firmware/boards/f0_module working-directory: ./firmware/boards/${{matrix.build-target}}
run: ./build_wideband.sh run: ./build_wideband.sh
- name: Attach binaries
uses: actions/upload-artifact@v2
with:
name: Wideband ${{matrix.build-target}}
path: ./firmware/build/wideband.*

13
README.md Normal file
View File

@ -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).

View File

@ -131,6 +131,7 @@ CSRC = $(ALLCSRC) $(BOARDDIR)/board.c
# setting. # setting.
CPPSRC = $(ALLCPPSRC) \ CPPSRC = $(ALLCPPSRC) \
$(BOARDDIR)/port.cpp \ $(BOARDDIR)/port.cpp \
$(BOARDDIR)/port_shared.cpp \
shared/flash.cpp \ shared/flash.cpp \
can.cpp \ can.cpp \
can_helper.cpp \ can_helper.cpp \
@ -152,7 +153,7 @@ ASMSRC = $(ALLASMSRC)
ASMXSRC = $(ALLXASMSRC) ASMXSRC = $(ALLXASMSRC)
# Inclusion directories. # Inclusion directories.
INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) boards/ INCDIR = $(CONFDIR) $(ALLINC) boards/ $(BOARDDIR)/io/
# Define C warning options here. # Define C warning options here.
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes

View File

@ -119,7 +119,11 @@ CSRC = $(ALLCSRC) cfg/board.c
# C++ sources that can be compiled in ARM or THUMB mode depending on the global # C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting. # 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. # List ASM source files here.
ASMSRC = $(ALLASMSRC) ASMSRC = $(ALLASMSRC)
@ -128,7 +132,8 @@ ASMSRC = $(ALLASMSRC)
ASMXSRC = $(ALLXASMSRC) ASMXSRC = $(ALLXASMSRC)
# Inclusion directories. # Inclusion directories.
INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) $(SRCDIR)/shared/ INCDIR = $(CONFDIR) $(ALLINC) $(SRCDIR)/shared/ \
../io ../..
# Define C warning options here. # Define C warning options here.
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes

View File

@ -1,7 +1,7 @@
#include "ch.h"
#include "hal.h"
#include "port_shared.h"
#include "flash.h" #include "flash.h"
#include "io_pins.h"
#include <cstring> #include <cstring>
@ -36,15 +36,18 @@ void boot_app() {
const uint32_t* appFlash = __appflash_start__; 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 // copy vector table to sram
// TODO: use __ram_vectors_size__ // TODO: use __ram_vectors_size__
memcpy(reinterpret_cast<char*>(&__ram_vectors_start__), appFlash, 256); memcpy(reinterpret_cast<char*>(&__ram_vectors_start__), appFlash, 256);
// The reset vector is at offset 4 (second uint32) // M0 core version, newer cores do same thing a bit nicer
uint32_t reset_vector = appFlash[1];
// switch to use vectors in ram // switch to use vectors in ram
SYSCFG->CFGR1 |= 3; SYSCFG->CFGR1 |= 3;
#endif
// TODO: is this necessary? // TODO: is this necessary?
//uint32_t app_msp = appLocation[0]; //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_WORKING_AREA(waBootloaderThread, 512);
THD_FUNCTION(BootloaderThread, arg) THD_FUNCTION(BootloaderThread, arg)
{ {
@ -244,24 +241,23 @@ int main(void) {
chThdCreateStatic(waBootloaderThread, sizeof(waBootloaderThread), NORMALPRIO + 1, BootloaderThread, nullptr); chThdCreateStatic(waBootloaderThread, sizeof(waBootloaderThread), NORMALPRIO + 1, BootloaderThread, nullptr);
// PB5 is blue LED palSetPadMode(LED_BLUE_PORT, LED_BLUE_PIN, PAL_MODE_OUTPUT_PUSHPULL);
palSetPadMode(GPIOB, 5, PAL_MODE_OUTPUT_PUSHPULL);
// PB6 is green LED palSetPadMode(LED_GREEN_PORT, LED_GREEN_PIN, PAL_MODE_OUTPUT_PUSHPULL);
palSetPadMode(GPIOB, 6, PAL_MODE_OUTPUT_PUSHPULL); palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
palTogglePad(GPIOB, 6);
for (size_t i = 0; i < 20; i++) for (size_t i = 0; i < 20; i++)
{ {
palTogglePad(GPIOB, 5); palTogglePad(LED_BLUE_PORT, LED_BLUE_PIN);
palTogglePad(GPIOB, 6); palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
chThdSleepMilliseconds(40); chThdSleepMilliseconds(40);
} }
// Block until booting the app is allowed and CRC matches // Block until booting the app is allowed and CRC matches
while (bootloaderBusy || !isAppValid()) while (bootloaderBusy || !isAppValid())
{ {
palTogglePad(GPIOB, 5); palTogglePad(LED_BLUE_PORT, LED_BLUE_PIN);
palTogglePad(GPIOB, 6); palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
chThdSleepMilliseconds(200); chThdSleepMilliseconds(200);
} }

View File

@ -1,5 +1,7 @@
#!/bin/bash #!/bin/bash
set -e
# first build the bootloader # first build the bootloader
cd bootloader cd bootloader
./build_bootloader.sh ./build_bootloader.sh
@ -8,11 +10,11 @@ cd bootloader
cd ../../.. cd ../../..
# delete the elf to force a re-link (it might not pick up the bootloader otherwise) # delete the elf to force a re-link (it might not pick up the bootloader otherwise)
rm -r build/ rm -rf build/
rm ../for_rusefi/wideband_image.h rm -f ../for_rusefi/wideband_image.h
# build app firmware! # 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) # 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 dd if=build/wideband.bin of=build/wideband_noboot_no_pad.bin skip=6144 bs=1

View File

@ -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

View File

@ -97,8 +97,8 @@ Configuration GetConfiguration()
} }
// Now, override the index with a hardware-strapped option (if present) // Now, override the index with a hardware-strapped option (if present)
auto sel1 = readSelPin(GPIOB, 1); auto sel1 = readSelPin(ID_SEL1_PORT, ID_SEL1_PIN);
auto sel2 = readSelPin(GPIOA, 8); auto sel2 = readSelPin(ID_SEL2_PORT, ID_SEL2_PIN);
// See https://github.com/mck1117/wideband/issues/11 // See https://github.com/mck1117/wideband/issues/11
switch (3 * sel1 + sel2) { switch (3 * sel1 + sel2) {
@ -129,9 +129,3 @@ void SetConfiguration(const Configuration& newConfig)
sizeof(Configuration) 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),
};

View File

@ -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),
};

View File

@ -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)

View File

@ -17,30 +17,26 @@
#ifndef _BOARD_H_ #ifndef _BOARD_H_
#define _BOARD_H_ #define _BOARD_H_
/*
* Setup for the ST INEMO-M1 Discovery board.
*/
/* /*
* Board identifier. * Board identifier.
*/ */
#define BOARD_ST_NUCLEO64_F103RB #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) #if !defined(STM32_HSECLK)
#define STM32_HSECLK 8000000 #define STM32_HSECLK 0U
#define STM32_HSE_BYPASS
#elif defined(NUCLEO_HSE_CRYSTAL)
#define STM32_HSECLK 8000000
#else
#define STM32_HSECLK 0
#endif #endif
/* /*
@ -191,20 +187,28 @@
/* /*
* Port A setup. * Port A setup.
* Everything input with pull-up except: * Everything input with pull-up except:
* PA2 - Alternate output (GPIOA_ARD_D1, GPIOA_USART2_TX). * PA0 - Ip_sense (analog in).
* PA3 - Normal input (GPIOA_ARD_D0, GPIOA_USART2_RX). * PA1 - Ip_dac (PWM) (output pushpull, alternate).
* PA5 - Push Pull output (GPIOA_LED_GREEN). * 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). * PA13 - Pull-up input (GPIOA_SWDIO).
* PA14 - Pull-down input (GPIOA_SWCLK). * PA14 - Pull-down input (GPIOA_SWCLK).
*/ */
#define VAL_GPIOACRL 0x88384B88 /* PA7...PA0 */ #define VAL_GPIOACRL 0x88800020 /* PA7...PA0 */
#define VAL_GPIOACRH 0x88888888 /* PA15...PA8 */ #define VAL_GPIOACRH 0x888B88B2 /* PA15...PA8 */
#define VAL_GPIOAODR 0xFFFFBFDF #define VAL_GPIOAODR 0xFFFFFFFF
/* /*
* Port B setup. * Port B setup.
* Everything input with pull-up except: * 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_GPIOBCRL 0x88888888 /* PB7...PB0 */
#define VAL_GPIOBCRH 0x88888888 /* PB15...PB8 */ #define VAL_GPIOBCRH 0x88888888 /* PB15...PB8 */
@ -213,19 +217,16 @@
/* /*
* Port C setup. * Port C setup.
* Everything input with pull-up except: * Everything input with pull-up except:
* PC13 - Normal input (GPIOC_BUTTON).
*/ */
#define VAL_GPIOCCRL 0x88888888 /* PC7...PC0 */ #define VAL_GPIOCCRL 0x88888888 /* PC7...PC0 */
#define VAL_GPIOCCRH 0x88488888 /* PC15...PC8 */ #define VAL_GPIOCCRH 0x88888888 /* PC15...PC8 */
#define VAL_GPIOCODR 0xFFFFFFFF #define VAL_GPIOCODR 0xFFFFFFFF
/* /*
* Port D setup. * Port D setup.
* Everything input with pull-up except: * 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_GPIODCRH 0x88888888 /* PD15...PD8 */
#define VAL_GPIODODR 0xFFFFFFFF #define VAL_GPIODODR 0xFFFFFFFF

View File

@ -1,3 +1,5 @@
USE_BOOTLOADER = no
MCU = cortex-m3 MCU = cortex-m3
include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f1xx.mk include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f1xx.mk

View File

@ -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

View File

@ -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

View File

@ -35,6 +35,8 @@
/* /*
* HAL driver system settings. * 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_NO_INIT FALSE
#define STM32_HSI_ENABLED TRUE #define STM32_HSI_ENABLED TRUE
@ -47,7 +49,7 @@
#define STM32_PLLMUL_VALUE 12 #define STM32_PLLMUL_VALUE 12
#define STM32_HPRE STM32_HPRE_DIV1 #define STM32_HPRE STM32_HPRE_DIV1
#define STM32_PPRE1 STM32_PPRE1_DIV2 #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_ADCPRE STM32_ADCPRE_DIV4
#define STM32_USB_CLOCK_REQUIRED TRUE #define STM32_USB_CLOCK_REQUIRED TRUE
#define STM32_USBPRE STM32_USBPRE_DIV1 #define STM32_USBPRE STM32_USBPRE_DIV1
@ -133,8 +135,8 @@
*/ */
#define STM32_PWM_USE_ADVANCED FALSE #define STM32_PWM_USE_ADVANCED FALSE
#define STM32_PWM_USE_TIM1 TRUE #define STM32_PWM_USE_TIM1 TRUE
#define STM32_PWM_USE_TIM2 FALSE #define STM32_PWM_USE_TIM2 TRUE
#define STM32_PWM_USE_TIM3 TRUE #define STM32_PWM_USE_TIM3 FALSE
#define STM32_PWM_USE_TIM4 FALSE #define STM32_PWM_USE_TIM4 FALSE
#define STM32_PWM_USE_TIM5 FALSE #define STM32_PWM_USE_TIM5 FALSE
#define STM32_PWM_USE_TIM8 FALSE #define STM32_PWM_USE_TIM8 FALSE
@ -182,7 +184,7 @@
* ST driver system settings. * ST driver system settings.
*/ */
#define STM32_ST_IRQ_PRIORITY 8 #define STM32_ST_IRQ_PRIORITY 8
#define STM32_ST_USE_TIMER 2 #define STM32_ST_USE_TIMER 3
/* /*
* UART driver system settings. * UART driver system settings.

View File

@ -1,16 +1,58 @@
#include "port.h" #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() AnalogResult AnalogSample()
{ {
// TODO: implement me! adcConvert(&ADCD1, &convGroup, adcBuffer, ADC_OVERSAMPLE);
return {};
}
const CANConfig canConfig500 = return
{ {
CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP, .NernstVoltage = AverageSamples(adcBuffer, 0) * NERNST_INPUT_GAIN,
0 // TODO: set bit timing! correctly! .PumpCurrentVoltage = AverageSamples(adcBuffer, 1),
}; .VirtualGroundVoltageInt = AverageSamples(adcBuffer, 2),
};
}
Configuration GetConfiguration() Configuration GetConfiguration()
{ {

View File

@ -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),
};

View File

@ -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)

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "hal.h" #include "hal.h"
#include "port_shared.h"
struct AnalogResult struct AnalogResult
{ {
@ -33,4 +34,3 @@ public:
Configuration GetConfiguration(); Configuration GetConfiguration();
void SetConfiguration(const Configuration& newConfig); void SetConfiguration(const Configuration& newConfig);
extern const CANConfig canConfig500;

View File

@ -0,0 +1,4 @@
#include "ch.h"
#include "hal.h"
extern const CANConfig canConfig500;

View File

@ -1,6 +1,7 @@
#include "can.h" #include "can.h"
#include "hal.h" #include "hal.h"
#include "fault.h"
#include "can_helper.h" #include "can_helper.h"
#include "heater_control.h" #include "heater_control.h"
#include "lambda_conversion.h" #include "lambda_conversion.h"
@ -8,6 +9,9 @@
#include "pump_dac.h" #include "pump_dac.h"
#include "port.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; Configuration configuration;
static THD_WORKING_AREA(waCanTxThread, 256); static THD_WORKING_AREA(waCanTxThread, 256);
@ -15,7 +19,7 @@ void CanTxThread(void*)
{ {
while(1) while(1)
{ {
SendEmulatedAemXseries(configuration.CanIndexOffset); SendRusefiFormat(configuration.CanIndexOffset);
chThdSleepMilliseconds(10); chThdSleepMilliseconds(10);
} }
@ -26,7 +30,7 @@ static void SendAck()
CANTxFrame frame; CANTxFrame frame;
frame.IDE = CAN_IDE_EXT; frame.IDE = CAN_IDE_EXT;
frame.EID = 0x727573; // ascii "rus" frame.EID = WB_ACK;
frame.RTR = CAN_RTR_DATA; frame.RTR = CAN_RTR_DATA;
frame.DLC = 0; frame.DLC = 0;
@ -53,7 +57,7 @@ void CanRxThread(void*)
continue; 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 // This is status from ECU - battery voltage and heater enable signal
// data0 contains battery voltage in tenths of a volt // data0 contains battery voltage in tenths of a volt
@ -65,7 +69,7 @@ void CanRxThread(void*)
SetHeaterAllowed(heaterAllowed); SetHeaterAllowed(heaterAllowed);
} }
// If it's a bootloader entry request, reboot to the bootloader! // 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(); SendAck();
@ -75,7 +79,7 @@ void CanRxThread(void*)
NVIC_SystemReset(); NVIC_SystemReset();
} }
// Check if it's an "index set" message // 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(); auto newCfg = GetConfiguration();
newCfg.CanIndexOffset = frame.data8[0]; newCfg.CanIndexOffset = frame.data8[0];
@ -95,61 +99,35 @@ void InitCan()
chThdCreateStatic(waCanRxThread, sizeof(waCanRxThread), NORMALPRIO - 4, CanRxThread, nullptr); 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)) #define SWAP_UINT16(x) (((x) << 8) | ((x) >> 8))
void SendEmulatedAemXseries(uint8_t idx) { void SendRusefiFormat(uint8_t idx)
CanTxMessage frame(0x180 + idx, 8, true); {
auto baseAddress = 0x190 + 2 * idx;
auto esr = GetSensorInternalResistance();
bool isValid = IsRunningClosedLoop(); {
CanTxTyped<wbo::StandardData> frame(baseAddress + 0);
float lambda = GetLambda(); // The same header is imported by the ECU and checked against this data in the frame
uint16_t intLambda = lambda * 10000; frame.get().Version = RUSEFI_WIDEBAND_VERSION;
// swap endian uint16_t lambda = GetLambda() * 10000;
intLambda = SWAP_UINT16(intLambda); frame.get().Lambda = lambda;
*reinterpret_cast<uint16_t*>(&frame[0]) = intLambda;
// bit 1 = LSU 4.9 detected // TODO: decode temperature instead of putting ESR here
// bit 7 = reading valid frame.get().TemperatureC = esr;
frame[6] = 0x02 | (isValid ? 0x80 : 0x00);
// Hijack a reserved bit to indicate that we're NOT an AEM controller frame.get().Valid = IsRunningClosedLoop() ? 0x01 : 0x00;
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; {
CanTxTyped<wbo::DiagData> frame(baseAddress + 1);
// Report current nernst voltage in byte 4, 5mv steps frame.get().Esr = esr;
frame[4] = (int)(GetNernstDc() * 200); frame.get().NernstDc = GetNernstDc() * 1000;
} frame.get().PumpDuty = GetPumpOutputDuty() * 255;
frame.get().Status = GetCurrentFault();
void SendCanData(float lambda, uint16_t measuredResistance) frame.get().HeaterDuty = GetHeaterDuty() * 255;
{ }
CanTxTyped<StandardDataFrame> frame(0x130);
frame.get().lambda = lambda * 10000;
frame.get().measuredResistance = measuredResistance;
} }

View File

@ -4,4 +4,4 @@
void InitCan(); void InitCan();
void SendCanData(float lambda, uint16_t measuredResistance); void SendCanData(float lambda, uint16_t measuredResistance);
void SendEmulatedAemXseries(uint8_t idx); void SendRusefiFormat(uint8_t idx);

View File

@ -1,18 +1,20 @@
#include "fault.h" #include "fault.h"
using namespace wbo;
static Fault currentFault = Fault::None; static Fault currentFault = Fault::None;
void setFault(Fault fault) void SetFault(Fault fault)
{ {
currentFault = fault; currentFault = fault;
} }
bool hasFault() bool HasFault()
{ {
return currentFault != Fault::None; return currentFault != Fault::None;
} }
Fault getCurrentFault() Fault GetCurrentFault()
{ {
return currentFault; return currentFault;
} }

View File

@ -1,15 +1,9 @@
#pragma once #pragma once
enum class Fault #include <cstdint>
{
None = 0,
// First fault code at 3 so it's easier to see #include "../for_rusefi/wideband_can.h"
SensorDidntHeat = 3,
SensorOverheat = 4,
SensorUnderheat = 5,
};
void setFault(Fault fault); void SetFault(wbo::Fault fault);
bool hasFault(); bool HasFault();
Fault getCurrentFault(); wbo::Fault GetCurrentFault();

View File

@ -9,8 +9,10 @@
#include "sampling.h" #include "sampling.h"
#include "pid.h" #include "pid.h"
using namespace wbo;
// 400khz / 1024 = 390hz PWM // 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 enum class HeaterState
{ {
@ -61,7 +63,7 @@ static HeaterState GetNextState(HeaterState state, float sensorEsr)
} }
else if (timeCounter == 0) else if (timeCounter == 0)
{ {
setFault(Fault::SensorDidntHeat); SetFault(Fault::SensorDidntHeat);
return HeaterState::Stopped; return HeaterState::Stopped;
} }
@ -69,14 +71,15 @@ static HeaterState GetNextState(HeaterState state, float sensorEsr)
break; break;
case HeaterState::ClosedLoop: case HeaterState::ClosedLoop:
// Check that the sensor's ESR is acceptable for normal operation
if (sensorEsr < HEATER_OVERHEAT_ESR) if (sensorEsr < HEATER_OVERHEAT_ESR)
{ {
setFault(Fault::SensorOverheat); SetFault(Fault::SensorOverheat);
return HeaterState::Stopped; return HeaterState::Stopped;
} }
else if (sensorEsr > HEATER_UNDERHEAT_ESR) else if (sensorEsr > HEATER_UNDERHEAT_ESR)
{ {
setFault(Fault::SensorUnderheat); SetFault(Fault::SensorUnderheat);
return HeaterState::Stopped; return HeaterState::Stopped;
} }
@ -201,3 +204,8 @@ void SetHeaterAllowed(bool allowed)
{ {
heaterAllowed = allowed; heaterAllowed = allowed;
} }
float GetHeaterDuty()
{
return heaterPwm.GetLastDuty();
}

View File

@ -6,3 +6,4 @@ void StartHeaterControl();
bool IsRunningClosedLoop(); bool IsRunningClosedLoop();
void SetBatteryVoltage(float vbatt); void SetBatteryVoltage(float vbatt);
void SetHeaterAllowed(bool allowed); void SetHeaterAllowed(bool allowed);
float GetHeaterDuty();

View File

@ -8,7 +8,9 @@
#include "pump_dac.h" #include "pump_dac.h"
#include "sampling.h" #include "sampling.h"
#include "uart.h" #include "uart.h"
#include "io_pins.h"
using namespace wbo;
/* /*
* Application entry point. * Application entry point.
@ -24,19 +26,21 @@ int main() {
StartPumpControl(); StartPumpControl();
InitCan(); InitCan();
//InitUart(); #ifdef ECHO_UART
InitUart();
#endif
while(true) while(true)
{ {
auto fault = getCurrentFault(); auto fault = GetCurrentFault();
if (fault == Fault::None) if (fault == Fault::None)
{ {
// blue is off // blue is off
palClearPad(GPIOB, 5); palClearPad(LED_BLUE_PORT, LED_BLUE_PIN);
// Green is blinking // Green is blinking
palTogglePad(GPIOB, 6); palTogglePad(LED_GREEN_PORT, LED_GREEN_PIN);
// Slow blink if closed loop, fast if not // Slow blink if closed loop, fast if not
chThdSleepMilliseconds(IsRunningClosedLoop() ? 700 : 50); chThdSleepMilliseconds(IsRunningClosedLoop() ? 700 : 50);
@ -44,13 +48,13 @@ int main() {
else else
{ {
// green is off // green is off
palClearPad(GPIOB, 6); palClearPad(LED_GREEN_PORT, LED_GREEN_PIN);
// Blink out the error code // Blink out the error code
for (int i = 0; i < 2 * static_cast<int>(fault); i++) for (int i = 0; i < 2 * static_cast<int>(fault); i++)
{ {
// Blue is blinking // Blue is blinking
palTogglePad(GPIOB, 5); palTogglePad(LED_BLUE_PORT, LED_BLUE_PIN);
// fast blink // fast blink
chThdSleepMilliseconds(300); chThdSleepMilliseconds(300);

View File

@ -7,7 +7,7 @@
#include "hal.h" #include "hal.h"
// 48MHz / 1024 = 46.8khz PWM // 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() void InitPumpDac()
{ {
@ -37,7 +37,7 @@ void SetPumpCurrentTarget(int32_t microampere)
pumpDac.SetDuty(volts / VCC_VOLTS); pumpDac.SetDuty(volts / VCC_VOLTS);
} }
uint16_t GetPumpOutputDuty() float GetPumpOutputDuty()
{ {
return pumpDac.GetLastDuty(); return pumpDac.GetLastDuty();
} }

View File

@ -4,4 +4,4 @@
void InitPumpDac(); void InitPumpDac();
void SetPumpCurrentTarget(int32_t microamperes); void SetPumpCurrentTarget(int32_t microamperes);
uint16_t GetPumpOutputDuty(); float GetPumpOutputDuty();

View File

@ -45,14 +45,14 @@ float clampF(float min, float clamp, float max) {
} }
void Pwm::SetDuty(float duty) { void Pwm::SetDuty(float duty) {
pwmcnt_t highTime = m_counterPeriod * clampF(0, duty, 1); auto dutyFloat = clampF(0, duty, 1);
m_dutyFloat = dutyFloat;
m_lastDuty = highTime; pwmcnt_t highTime = m_counterPeriod * dutyFloat;
pwm_lld_enable_channel(m_driver, m_channel, highTime); pwm_lld_enable_channel(m_driver, m_channel, highTime);
} }
uint16_t Pwm::GetLastDuty() const float Pwm::GetLastDuty() const
{ {
return m_lastDuty; return m_dutyFloat;
} }

View File

@ -11,11 +11,12 @@ public:
void Start(); void Start();
void SetDuty(float duty); void SetDuty(float duty);
uint16_t GetLastDuty() const; float GetLastDuty() const;
private: private:
PWMDriver* const m_driver; PWMDriver* const m_driver;
const uint8_t m_channel; const uint8_t m_channel;
const uint32_t m_counterFrequency; const uint32_t m_counterFrequency;
const uint16_t m_counterPeriod; const uint16_t m_counterPeriod;
uint16_t m_lastDuty; float m_dutyFloat;
}; };

View File

@ -6,11 +6,12 @@
#include "wideband_config.h" #include "wideband_config.h"
#include "port.h" #include "port.h"
#include "io_pins.h"
// Stored results // Stored results
float nernstAc = 0; static float nernstAc = 0;
float nernstDc = 0; static float nernstDc = 0;
volatile float pumpCurrentSenseVoltage = 0; static volatile float pumpCurrentSenseVoltage = 0;
constexpr float f_abs(float x) constexpr float f_abs(float x)
{ {
@ -29,7 +30,7 @@ static void SamplingThread(void*)
auto result = AnalogSample(); auto result = AnalogSample();
// Toggle the pin after sampling so that any switching noise occurs while we're doing our math instead of when sampling // 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; float r_1 = result.NernstVoltage;

View File

@ -1,15 +1,11 @@
#pragma once #pragma once
// Fundamental board constants #include "io_pins.h"
#define VCC_VOLTS (3.3f) #include "wideband_board_config.h"
#define HALF_VCC (VCC_VOLTS / 2)
#define ADC_MAX_COUNT (4095)
#define ADC_OVERSAMPLE 24
// ******************************* // *******************************
// Nernst voltage & ESR sense // Nernst voltage & ESR sense
// ******************************* // *******************************
#define NERNST_INPUT_GAIN (1 / 2.7f)
// Nernst AC injection resistor value // Nernst AC injection resistor value
#define ESR_SUPPLY_R (22000) #define ESR_SUPPLY_R (22000)
@ -42,9 +38,9 @@
#define HEATER_CONTROL_PERIOD 50 #define HEATER_CONTROL_PERIOD 50
#define HEATER_PREHEAT_TIME 5000 #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_TARGET_ESR 300
#define HEATER_OVERHEAT_ESR 150 #define HEATER_OVERHEAT_ESR 150
#define HEATER_UNDERHEAT_ESR 600 #define HEATER_UNDERHEAT_ESR 700

60
for_rusefi/wideband_can.h Normal file
View File

@ -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