Sync: commaai/panda:master into sunnypilot/panda:master

This commit is contained in:
Jason Wen
2025-08-01 23:57:57 -04:00
committed by GitHub
81 changed files with 639 additions and 1907 deletions

View File

@@ -80,4 +80,4 @@ jobs:
- name: tests/misra/install.sh
run: ${{ env.RUN }} "cd tests/misra && ./install.sh"
- name: MISRA mutation tests
run: ${{ env.RUN }} "cd tests/misra && pytest -n2 test_mutation.py"
run: ${{ env.RUN }} "cd tests/misra && pytest test_mutation.py"

2
.gitignore vendored
View File

@@ -22,9 +22,11 @@ nosetests.xml
.mypy_cache/
.sconsign.dblite
uv.lock
compile_commands.json
# CTU info files generated by Cppcheck
*.*.ctu-info
cppcheck-addon-ctu-file-list
# safety coverage-related files
*.gcda

View File

@@ -1,21 +1,19 @@
FROM ubuntu:24.04
ENV WORKDIR=/tmp/panda/
ENV PYTHONUNBUFFERED=1
ENV PYTHONPATH=/tmp/pythonpath
ENV PATH="$WORKDIR/.venv/bin:$PATH"
WORKDIR $WORKDIR
# deps install
COPY pyproject.toml __init__.py setup.sh /tmp/
COPY python/__init__.py /tmp/python/
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends sudo && /tmp/setup.sh
COPY pyproject.toml __init__.py setup.sh $WORKDIR
RUN mkdir -p $WORKDIR/python/ && touch $WORKDIR/__init__.py
RUN apt-get update && apt-get install -y --no-install-recommends sudo && DEBIAN_FRONTEND=noninteractive $WORKDIR/setup.sh
COPY pyproject.toml __init__.py $PYTHONPATH/panda/
COPY python/__init__.py $PYTHONPATH/panda/python/
RUN pip3 install --break-system-packages --no-cache-dir $PYTHONPATH/panda/[dev]
# second pass for the opendbc moving tag
ARG CACHEBUST=1
RUN DEBIAN_FRONTEND=noninteractive $WORKDIR/setup.sh
RUN git config --global --add safe.directory $PYTHONPATH/panda
# for Jenkins
COPY README.md panda.tar.* /tmp/
RUN mkdir -p /tmp/pythonpath/panda && \
tar -xvf /tmp/panda.tar.gz -C /tmp/pythonpath/panda/ || true
RUN git config --global --add safe.directory $WORKDIR/panda
COPY . $WORKDIR

15
Jenkinsfile vendored
View File

@@ -4,7 +4,6 @@ def docker_run(String step_label, int timeout_mins, String cmd) {
--env PYTHONWARNINGS=error \
--volume /dev/bus/usb:/dev/bus/usb \
--volume /var/run/dbus:/var/run/dbus \
--workdir /tmp/pythonpath/panda \
--net host \
${env.DOCKER_IMAGE_TAG} \
bash -c 'scons -j8 && ${cmd}'", \
@@ -35,6 +34,9 @@ export PYTHONPATH=${env.TEST_DIR}/../
export PYTHONWARNINGS=error
ln -sf /data/openpilot/opendbc_repo/opendbc /data/opendbc
# TODO: this is an agnos issue
export PYTEST_ADDOPTS="-p no:asyncio"
cd ${env.TEST_DIR} || true
${cmd}
exit 0
@@ -83,8 +85,7 @@ pipeline {
steps {
timeout(time: 20, unit: 'MINUTES') {
script {
sh 'git archive -v -o panda.tar.gz --format=tar.gz HEAD'
dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}")
dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}", "--build-arg CACHEBUST=${env.BUILD_NUMBER} .")
}
}
}
@@ -108,7 +109,7 @@ pipeline {
["build", "scons -j4"],
["flash", "cd scripts/ && ./reflash_internal_panda.py"],
["flash jungle", "cd board/jungle && ./flash.py --all"],
["test", "cd tests/hitl && HW_TYPES=10 pytest -n0 --durations=0 2*.py [5-9]*.py"],
["test", "cd tests/hitl && HW_TYPES=10 pytest --durations=0 2*.py [5-9]*.py"],
])
}
}
@@ -120,7 +121,7 @@ pipeline {
["build", "scons -j4"],
["flash", "cd scripts/ && ./reflash_internal_panda.py"],
["flash jungle", "cd board/jungle && ./flash.py --all"],
["test", "cd tests/hitl && HW_TYPES=9 pytest -n0 --durations=0 2*.py [5-9]*.py"],
["test", "cd tests/hitl && HW_TYPES=9 pytest --durations=0 2*.py [5-9]*.py"],
])
}
}
@@ -132,7 +133,7 @@ pipeline {
["build", "scons -j4"],
["flash", "cd scripts/ && ./reflash_internal_panda.py"],
["flash jungle", "cd board/jungle && ./flash.py --all"],
["test", "cd tests/hitl && HW_TYPES=6 pytest -n0 --durations=0 [2-9]*.py -k 'not test_send_recv'"],
["test", "cd tests/hitl && HW_TYPES=6 pytest --durations=0 [2-9]*.py -k 'not test_send_recv'"],
])
}
}
@@ -140,7 +141,7 @@ pipeline {
stage('bootkick tests') {
steps {
script {
docker_run("test", 10, "pytest -n0 ./tests/som/test_bootkick.py")
docker_run("test", 10, "pytest ./tests/som/test_bootkick.py")
}
}
}

View File

@@ -7,8 +7,6 @@ BUILDER = "DEV"
common_flags = []
panda_root = Dir('.')
if os.getenv("RELEASE"):
BUILD_TYPE = "RELEASE"
cert_fn = os.getenv("CERT")
@@ -62,10 +60,10 @@ def to_c_uint32(x):
return "{" + 'U,'.join(map(str, nums)) + "U}"
def build_project(project_name, project, extra_flags):
linkerscript_fn = File(project["LINKER_SCRIPT"]).srcnode().relpath
def build_project(project_name, project, main, extra_flags):
project_dir = Dir(f'./board/obj/{project_name}/')
flags = project["PROJECT_FLAGS"] + extra_flags + common_flags + [
flags = project["FLAGS"] + extra_flags + common_flags + [
"-Wall",
"-Wextra",
"-Wstrict-prototypes",
@@ -76,15 +74,10 @@ def build_project(project_name, project, extra_flags):
"-fno-builtin",
"-std=gnu11",
"-fmax-errors=1",
f"-T{linkerscript_fn}",
]
includes = [
'.',
'..',
panda_root,
f"{panda_root}/board/",
opendbc.INCLUDE_PATH,
f"-T{File(project['LINKER_SCRIPT']).srcnode().relpath}",
"-fsingle-precision-constant",
"-Os",
"-g",
]
env = Environment(
@@ -93,10 +86,11 @@ def build_project(project_name, project, extra_flags):
AS=PREFIX + 'gcc',
OBJCOPY=PREFIX + 'objcopy',
OBJDUMP=PREFIX + 'objdump',
OBJPREFIX=project_dir,
CFLAGS=flags,
ASFLAGS=flags,
LINKFLAGS=flags,
CPPPATH=includes,
CPPPATH=[Dir("./"), "./board/stm32f4/inc", "./board/stm32h7/inc", opendbc.INCLUDE_PATH],
ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES",
BUILDERS={
'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf')
@@ -104,68 +98,57 @@ def build_project(project_name, project, extra_flags):
tools=["default", "compilation_db"],
)
startup = env.Object(f"obj/startup_{project_name}", project["STARTUP_FILE"])
startup = env.Object(project["STARTUP_FILE"])
# Bootstub
crypto_obj = [
env.Object(f"rsa-{project_name}", f"{panda_root}/crypto/rsa.c"),
env.Object(f"sha-{project_name}", f"{panda_root}/crypto/sha.c")
]
bootstub_obj = env.Object(f"bootstub-{project_name}", File(project.get("BOOTSTUB", f"{panda_root}/board/bootstub.c")))
bootstub_elf = env.Program(f"obj/bootstub.{project_name}.elf",
[startup] + crypto_obj + [bootstub_obj])
env.Objcopy(f"obj/bootstub.{project_name}.bin", bootstub_elf)
# Build bootstub
bs_env = env.Clone()
bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB")
bs_elf = bs_env.Program(f"{project_dir}/bootstub.elf", [
startup,
"./crypto/rsa.c",
"./crypto/sha.c",
"./board/bootstub.c",
])
bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf)
# Build main
main_obj = env.Object(f"main-{project_name}", project["MAIN"])
main_elf = env.Program(f"obj/{project_name}.elf", [startup, main_obj],
LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
main_bin = env.Objcopy(f"obj/{project_name}.bin", main_elf)
# Sign main
sign_py = File(f"{panda_root}/crypto/sign.py").srcnode().relpath
env.Command(f"obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}")
# Build + sign main (aka app)
main_elf = env.Program(f"{project_dir}/main.elf", [
startup,
main
], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf)
sign_py = File(f"./crypto/sign.py").srcnode().relpath
env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}")
base_project_f4 = {
"MAIN": "main.c",
"STARTUP_FILE": File("./board/stm32f4/startup_stm32f413xx.s"),
"LINKER_SCRIPT": File("./board/stm32f4/stm32f4_flash.ld"),
"STARTUP_FILE": "./board/stm32f4/startup_stm32f413xx.s",
"LINKER_SCRIPT": "./board/stm32f4/stm32f4_flash.ld",
"APP_START_ADDRESS": "0x8004000",
"PROJECT_FLAGS": [
"FLAGS": [
"-mcpu=cortex-m4",
"-mhard-float",
"-DSTM32F4",
"-DSTM32F413xx",
"-Iboard/stm32f4/inc",
"-mfpu=fpv4-sp-d16",
"-fsingle-precision-constant",
"-Os",
"-g",
],
}
base_project_h7 = {
"MAIN": "main.c",
"STARTUP_FILE": File("./board/stm32h7/startup_stm32h7x5xx.s"),
"LINKER_SCRIPT": File("./board/stm32h7/stm32h7x5_flash.ld"),
"STARTUP_FILE": "./board/stm32h7/startup_stm32h7x5xx.s",
"LINKER_SCRIPT": "./board/stm32h7/stm32h7x5_flash.ld",
"APP_START_ADDRESS": "0x8020000",
"PROJECT_FLAGS": [
"FLAGS": [
"-mcpu=cortex-m7",
"-mhard-float",
"-DSTM32H7",
"-DSTM32H725xx",
"-Iboard/stm32h7/inc",
"-mfpu=fpv5-d16",
"-fsingle-precision-constant",
"-Os",
"-g",
],
}
Export('base_project_f4', 'base_project_h7', 'build_project')
# Common autogenerated includes
with open("board/obj/gitversion.h", "w") as f:
version = get_version(BUILDER, BUILD_TYPE)
@@ -181,10 +164,16 @@ with open("board/obj/cert.h", "w") as f:
f.write("\n".join(cert) + "\n")
# panda fw
SConscript('board/SConscript')
build_project("panda", base_project_f4, "./board/main.c", [])
build_project("panda_h7", base_project_h7, "./board/main.c", [])
# panda jungle fw
SConscript('board/jungle/SConscript')
flags = [
"-DPANDA_JUNGLE",
]
if os.getenv("FINAL_PROVISIONING"):
flags += ["-DFINAL_PROVISIONING"]
build_project("panda_jungle_h7", base_project_h7, "./board/jungle/main.c", flags)
# test files
if GetOption('extras'):

View File

@@ -1,18 +0,0 @@
import os
import copy
Import('build_project', 'base_project_f4', 'base_project_h7')
build_projects = {
"panda": base_project_f4,
"panda_h7": base_project_h7,
}
for project_name, project in build_projects.items():
flags = [
"-DPANDA",
]
if ("ENABLE_SPI" in os.environ or "h7" in project_name):
flags.append('-DENABLE_SPI')
build_project(project_name, project, flags)

View File

@@ -1,130 +0,0 @@
#pragma once
#include "board_declarations.h"
// /////////////////////////////// //
// Black Panda (STM32F4) + Harness //
// /////////////////////////////// //
static void black_enable_can_transceiver(uint8_t transceiver, bool enabled) {
switch (transceiver){
case 1U:
set_gpio_output(GPIOC, 1, !enabled);
break;
case 2U:
set_gpio_output(GPIOC, 13, !enabled);
break;
case 3U:
set_gpio_output(GPIOA, 0, !enabled);
break;
case 4U:
set_gpio_output(GPIOB, 10, !enabled);
break;
default:
print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n");
break;
}
}
static void black_set_usb_load_switch(bool enabled) {
set_gpio_output(GPIOB, 1, !enabled);
}
static void black_set_can_mode(uint8_t mode) {
black_enable_can_transceiver(2U, false);
black_enable_can_transceiver(4U, false);
switch (mode) {
case CAN_MODE_NORMAL:
case CAN_MODE_OBD_CAN2:
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) {
// B12,B13: disable OBD mode
set_gpio_mode(GPIOB, 12, MODE_INPUT);
set_gpio_mode(GPIOB, 13, MODE_INPUT);
// B5,B6: normal CAN2 mode
set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2);
set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2);
black_enable_can_transceiver(2U, true);
} else {
// B5,B6: disable normal CAN2 mode
set_gpio_mode(GPIOB, 5, MODE_INPUT);
set_gpio_mode(GPIOB, 6, MODE_INPUT);
// B12,B13: OBD mode
set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2);
set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2);
black_enable_can_transceiver(4U, true);
}
break;
default:
print("Tried to set unsupported CAN mode: "); puth(mode); print("\n");
break;
}
}
static bool black_check_ignition(void){
// ignition is checked through harness
return harness_check_ignition();
}
static void black_init(void) {
common_init_gpio();
// A8,A15: normal CAN3 mode
set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3);
set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3);
// GPS OFF
set_gpio_output(GPIOC, 5, 0);
set_gpio_output(GPIOC, 12, 0);
// Turn on USB load switch.
black_set_usb_load_switch(true);
}
static void black_init_bootloader(void) {
// GPS OFF
set_gpio_output(GPIOC, 5, 0);
set_gpio_output(GPIOC, 12, 0);
}
static harness_configuration black_harness_config = {
.has_harness = true,
.GPIO_SBU1 = GPIOC,
.GPIO_SBU2 = GPIOC,
.GPIO_relay_SBU1 = GPIOC,
.GPIO_relay_SBU2 = GPIOC,
.pin_SBU1 = 0,
.pin_SBU2 = 3,
.pin_relay_SBU1 = 10,
.pin_relay_SBU2 = 11,
.adc_channel_SBU1 = 10,
.adc_channel_SBU2 = 13
};
board board_black = {
.set_bootkick = unused_set_bootkick,
.harness_config = &black_harness_config,
.has_spi = false,
.has_canfd = false,
.fan_max_rpm = 0U,
.fan_max_pwm = 100U,
.avdd_mV = 3300U,
.fan_stall_recovery = false,
.fan_enable_cooldown_time = 0U,
.init = black_init,
.init_bootloader = black_init_bootloader,
.enable_can_transceiver = black_enable_can_transceiver,
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {9, 7, 6},
.set_can_mode = black_set_can_mode,
.check_ignition = black_check_ignition,
.read_voltage_mV = white_read_voltage_mV,
.read_current_mA = unused_read_current,
.set_fan_enabled = unused_set_fan_enabled,
.set_ir_power = unused_set_ir_power,
.set_siren = unused_set_siren,
.read_som_gpio = unused_read_som_gpio,
.set_amp_enabled = unused_set_amp_enabled
};

View File

@@ -14,7 +14,6 @@ typedef void (*board_init)(void);
typedef void (*board_init_bootloader)(void);
typedef void (*board_enable_can_transceiver)(uint8_t transceiver, bool enabled);
typedef void (*board_set_can_mode)(uint8_t mode);
typedef bool (*board_check_ignition)(void);
typedef uint32_t (*board_read_voltage_mV)(void);
typedef uint32_t (*board_read_current_mA)(void);
typedef void (*board_set_ir_power)(uint8_t percentage);
@@ -30,7 +29,6 @@ struct board {
const uint8_t led_pin[3];
const uint8_t led_pwm_channels[3]; // leave at 0 to disable PWM
const bool has_spi;
const bool has_canfd;
const uint16_t fan_max_rpm;
const uint16_t avdd_mV;
const bool fan_stall_recovery;
@@ -40,7 +38,6 @@ struct board {
board_init_bootloader init_bootloader;
board_enable_can_transceiver enable_can_transceiver;
board_set_can_mode set_can_mode;
board_check_ignition check_ignition;
board_read_voltage_mV read_voltage_mV;
board_read_current_mA read_current_mA;
board_set_ir_power set_ir_power;
@@ -54,30 +51,17 @@ struct board {
// ******************* Definitions ********************
// These should match the enums in cereal/log.capnp and __init__.py
#define HW_TYPE_UNKNOWN 0U
#define HW_TYPE_WHITE_PANDA 1U
//#define HW_TYPE_GREY_PANDA 2U
#define HW_TYPE_BLACK_PANDA 3U
//#define HW_TYPE_PEDAL 4U
//#define HW_TYPE_UNO 5U
#define HW_TYPE_DOS 6U
#define HW_TYPE_RED_PANDA 7U
#define HW_TYPE_RED_PANDA_V2 8U
#define HW_TYPE_TRES 9U
#define HW_TYPE_CUATRO 10U
// USB power modes (from cereal.log.health)
#define USB_POWER_NONE 0U
#define USB_POWER_CLIENT 1U
#define USB_POWER_CDP 2U
#define USB_POWER_DCP 3U
// CAN modes
#define CAN_MODE_NORMAL 0U
#define CAN_MODE_OBD_CAN2 1U
extern struct board board_black;
extern struct board board_dos;
extern struct board board_tres;
extern struct board board_white;
extern struct board board_cuatro;
extern struct board board_red;

View File

@@ -26,11 +26,11 @@ static void cuatro_enable_can_transceiver(uint8_t transceiver, bool enabled) {
}
static uint32_t cuatro_read_voltage_mV(void) {
return adc_get_mV(8) * 11U;
return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 8)) * 11U;
}
static uint32_t cuatro_read_current_mA(void) {
return adc_get_mV(3) * 2U;
return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 3)) * 2U;
}
static void cuatro_set_fan_enabled(bool enabled) {
@@ -39,12 +39,27 @@ static void cuatro_set_fan_enabled(bool enabled) {
static void cuatro_set_bootkick(BootState state) {
set_gpio_output(GPIOA, 0, state != BOOT_BOOTKICK);
// TODO: confirm we need this
//set_gpio_output(GPIOC, 12, state != BOOT_RESET);
}
static void cuatro_set_amp_enabled(bool enabled){
set_gpio_output(GPIOA, 5, enabled);
static void cuatro_set_amp_enabled(bool enabled) {
// *** tmp, remove soon ***
static const uint8_t olds[][12] = {
{0x44, 0x00, 0x10, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x14, 0x00, 0x13, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x04, 0x00, 0x30, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x2f, 0x00, 0x14, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x1e, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x26, 0x00, 0x15, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x35, 0x00, 0x32, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x37, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
};
bool is_old = false;
for (uint8_t i = 0U; i < (sizeof(olds) / sizeof(olds[0])); i++) {
is_old |= (memcmp(olds[i], ((uint8_t *)UID_BASE), 12) == 0);
}
if (is_old) set_gpio_output(GPIOA, 5, enabled);
// *** tmp end ***
set_gpio_output(GPIOB, 0, enabled);
}
@@ -102,7 +117,6 @@ static void cuatro_init(void) {
}
static harness_configuration cuatro_harness_config = {
.has_harness = true,
.GPIO_SBU1 = GPIOC,
.GPIO_SBU2 = GPIOA,
.GPIO_relay_SBU1 = GPIOA,
@@ -111,14 +125,13 @@ static harness_configuration cuatro_harness_config = {
.pin_SBU2 = 1,
.pin_relay_SBU1 = 9,
.pin_relay_SBU2 = 3,
.adc_channel_SBU1 = 4, // ADC12_INP4
.adc_channel_SBU2 = 17 // ADC1_INP17
.adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4),
.adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17)
};
board board_cuatro = {
.harness_config = &cuatro_harness_config,
.has_spi = true,
.has_canfd = true,
.fan_max_rpm = 12500U,
.fan_max_pwm = 99U, // it can go up to 14k RPM, but 99% -> 100% is very non-linear
.avdd_mV = 1800U,
@@ -131,7 +144,6 @@ board board_cuatro = {
.led_pin = {6, 7, 9},
.led_pwm_channels = {1, 2, 4},
.set_can_mode = tres_set_can_mode,
.check_ignition = red_check_ignition,
.read_voltage_mV = cuatro_read_voltage_mV,
.read_current_mA = cuatro_read_current_mA,
.set_fan_enabled = cuatro_set_fan_enabled,

View File

@@ -62,11 +62,6 @@ static void dos_set_can_mode(uint8_t mode) {
}
}
static bool dos_check_ignition(void){
// ignition is checked through harness
return harness_check_ignition();
}
static void dos_set_ir_power(uint8_t percentage){
pwm_set(TIM4, 2, percentage);
}
@@ -79,6 +74,10 @@ static void dos_set_siren(bool enabled){
set_gpio_output(GPIOC, 12, enabled);
}
static uint32_t dos_read_voltage_mV(void){
return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 12)) * 11U;
}
static bool dos_read_som_gpio (void){
return (get_gpio_input(GPIOC, 2) != 0);
}
@@ -110,7 +109,6 @@ static void dos_init(void) {
}
static harness_configuration dos_harness_config = {
.has_harness = true,
.GPIO_SBU1 = GPIOC,
.GPIO_SBU2 = GPIOC,
.GPIO_relay_SBU1 = GPIOC,
@@ -119,18 +117,13 @@ static harness_configuration dos_harness_config = {
.pin_SBU2 = 3,
.pin_relay_SBU1 = 10,
.pin_relay_SBU2 = 11,
.adc_channel_SBU1 = 10,
.adc_channel_SBU2 = 13
.adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 10),
.adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 13),
};
board board_dos = {
.harness_config = &dos_harness_config,
#ifdef ENABLE_SPI
.has_spi = true,
#else
.has_spi = false,
#endif
.has_canfd = false,
.fan_max_rpm = 6500U,
.fan_max_pwm = 100U,
.avdd_mV = 3300U,
@@ -142,8 +135,7 @@ board board_dos = {
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {9, 7, 6},
.set_can_mode = dos_set_can_mode,
.check_ignition = dos_check_ignition,
.read_voltage_mV = white_read_voltage_mV,
.read_voltage_mV = dos_read_voltage_mV,
.read_current_mA = unused_read_current,
.set_fan_enabled = dos_set_fan_enabled,
.set_ir_power = dos_set_ir_power,

View File

@@ -67,13 +67,8 @@ static void red_set_can_mode(uint8_t mode) {
}
}
static bool red_check_ignition(void) {
// ignition is checked through harness
return harness_check_ignition();
}
static uint32_t red_read_voltage_mV(void){
return adc_get_mV(2) * 11U; // TODO: is this correct?
return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 2)) * 11U;
}
static void red_init(void) {
@@ -104,7 +99,6 @@ static void red_init(void) {
}
static harness_configuration red_harness_config = {
.has_harness = true,
.GPIO_SBU1 = GPIOC,
.GPIO_SBU2 = GPIOA,
.GPIO_relay_SBU1 = GPIOC,
@@ -113,15 +107,14 @@ static harness_configuration red_harness_config = {
.pin_SBU2 = 1,
.pin_relay_SBU1 = 10,
.pin_relay_SBU2 = 11,
.adc_channel_SBU1 = 4, //ADC12_INP4
.adc_channel_SBU2 = 17 //ADC1_INP17
.adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4),
.adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17)
};
board board_red = {
.set_bootkick = unused_set_bootkick,
.harness_config = &red_harness_config,
.has_spi = false,
.has_canfd = true,
.fan_max_rpm = 0U,
.fan_max_pwm = 100U,
.avdd_mV = 3300U,
@@ -133,7 +126,6 @@ board board_red = {
.led_GPIO = {GPIOE, GPIOE, GPIOE},
.led_pin = {4, 3, 2},
.set_can_mode = red_set_can_mode,
.check_ignition = red_check_ignition,
.read_voltage_mV = red_read_voltage_mV,
.read_current_mA = unused_read_current,
.set_fan_enabled = unused_set_fan_enabled,

View File

@@ -133,7 +133,6 @@ static void tres_init(void) {
}
static harness_configuration tres_harness_config = {
.has_harness = true,
.GPIO_SBU1 = GPIOC,
.GPIO_SBU2 = GPIOA,
.GPIO_relay_SBU1 = GPIOA,
@@ -142,14 +141,13 @@ static harness_configuration tres_harness_config = {
.pin_SBU2 = 1,
.pin_relay_SBU1 = 8,
.pin_relay_SBU2 = 3,
.adc_channel_SBU1 = 4, // ADC12_INP4
.adc_channel_SBU2 = 17 // ADC1_INP17
.adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4),
.adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17)
};
board board_tres = {
.harness_config = &tres_harness_config,
.has_spi = true,
.has_canfd = true,
.fan_max_rpm = 6600U,
.fan_max_pwm = 100U,
.avdd_mV = 1800U,
@@ -161,7 +159,6 @@ board board_tres = {
.led_GPIO = {GPIOE, GPIOE, GPIOE},
.led_pin = {4, 3, 2},
.set_can_mode = tres_set_can_mode,
.check_ignition = red_check_ignition,
.read_voltage_mV = red_read_voltage_mV,
.read_current_mA = unused_read_current,
.set_fan_enabled = tres_set_fan_enabled,

View File

@@ -1,179 +0,0 @@
#pragma once
#include "board_declarations.h"
// ///////////////////// //
// White Panda (STM32F4) //
// ///////////////////// //
static void white_enable_can_transceiver(uint8_t transceiver, bool enabled) {
switch (transceiver){
case 1U:
set_gpio_output(GPIOC, 1, !enabled);
break;
case 2U:
set_gpio_output(GPIOC, 13, !enabled);
break;
case 3U:
set_gpio_output(GPIOA, 0, !enabled);
break;
default:
break;
}
}
static void white_set_usb_power_mode(uint8_t mode){
switch (mode) {
case USB_POWER_CLIENT:
// B2,A13: set client mode
set_gpio_output(GPIOB, 2, 0);
set_gpio_output(GPIOA, 13, 1);
break;
case USB_POWER_CDP:
// B2,A13: set CDP mode
set_gpio_output(GPIOB, 2, 1);
set_gpio_output(GPIOA, 13, 1);
break;
case USB_POWER_DCP:
// B2,A13: set DCP mode on the charger (breaks USB!)
set_gpio_output(GPIOB, 2, 0);
set_gpio_output(GPIOA, 13, 0);
break;
default:
print("Invalid usb power mode\n");
break;
}
}
static void white_set_can_mode(uint8_t mode){
if (mode == CAN_MODE_NORMAL) {
// B12,B13: disable GMLAN mode
set_gpio_mode(GPIOB, 12, MODE_INPUT);
set_gpio_mode(GPIOB, 13, MODE_INPUT);
// B3,B4: disable GMLAN mode
set_gpio_mode(GPIOB, 3, MODE_INPUT);
set_gpio_mode(GPIOB, 4, MODE_INPUT);
// B5,B6: normal CAN2 mode
set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2);
set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2);
// A8,A15: normal CAN3 mode
set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3);
set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3);
}
}
static uint32_t white_read_voltage_mV(void){
return adc_get_mV(12) * 11U;
}
static uint32_t white_read_current_mA(void){
// This isn't in mA, but we're keeping it for backwards compatibility
return adc_get_raw(13);
}
static bool white_check_ignition(void){
// ignition is on PA1
return !get_gpio_input(GPIOA, 1);
}
static void white_grey_init(void) {
common_init_gpio();
// C3: current sense
set_gpio_mode(GPIOC, 3, MODE_ANALOG);
// A1: started_alt
set_gpio_pullup(GPIOA, 1, PULL_UP);
// A2, A3: USART 2 for debugging
set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2);
set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2);
// A4, A5, A6, A7: SPI
set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1);
set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1);
set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1);
set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1);
// B12: GMLAN, ignition sense, pull up
set_gpio_pullup(GPIOB, 12, PULL_UP);
/* GMLAN mode pins:
M0(B15) M1(B14) mode
=======================
0 0 sleep
1 0 100kbit
0 1 high voltage wakeup
1 1 33kbit (normal)
*/
set_gpio_output(GPIOB, 14, 0);
set_gpio_output(GPIOB, 15, 0);
// B7: K-line enable
set_gpio_output(GPIOB, 7, 1);
// C12, D2: Setup K-line (UART5)
set_gpio_alternate(GPIOC, 12, GPIO_AF8_UART5);
set_gpio_alternate(GPIOD, 2, GPIO_AF8_UART5);
set_gpio_pullup(GPIOD, 2, PULL_UP);
// L-line enable
set_gpio_output(GPIOA, 14, 1);
// C10, C11: L-Line setup (USART3)
set_gpio_alternate(GPIOC, 10, GPIO_AF7_USART3);
set_gpio_alternate(GPIOC, 11, GPIO_AF7_USART3);
set_gpio_pullup(GPIOC, 11, PULL_UP);
// Init usb power mode
// Init in CDP mode only if panda is powered by 12V.
// Otherwise a PC would not be able to flash a standalone panda
if (white_read_voltage_mV() > 8000U) { // 8V threshold
white_set_usb_power_mode(USB_POWER_CDP);
} else {
white_set_usb_power_mode(USB_POWER_CLIENT);
}
// ESP/GPS off
set_gpio_output(GPIOC, 5, 0);
set_gpio_output(GPIOC, 14, 0);
}
static void white_grey_init_bootloader(void) {
// ESP/GPS off
set_gpio_output(GPIOC, 5, 0);
set_gpio_output(GPIOC, 14, 0);
}
static harness_configuration white_harness_config = {
.has_harness = false
};
board board_white = {
.set_bootkick = unused_set_bootkick,
.harness_config = &white_harness_config,
.has_spi = false,
.has_canfd = false,
.fan_max_rpm = 0U,
.fan_max_pwm = 100U,
.avdd_mV = 3300U,
.fan_stall_recovery = false,
.fan_enable_cooldown_time = 0U,
.init = white_grey_init,
.init_bootloader = white_grey_init_bootloader,
.enable_can_transceiver = white_enable_can_transceiver,
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {9, 7, 6},
.set_can_mode = white_set_can_mode,
.check_ignition = white_check_ignition,
.read_voltage_mV = white_read_voltage_mV,
.read_current_mA = white_read_current_mA,
.set_fan_enabled = unused_set_fan_enabled,
.set_ir_power = unused_set_ir_power,
.set_siren = unused_set_siren,
.read_som_gpio = unused_read_som_gpio,
.set_amp_enabled = unused_set_amp_enabled
};

View File

@@ -1,24 +1,22 @@
#define BOOTSTUB
#define VERS_TAG 0x53524556
#define MIN_VERSION 2
// ********************* Includes *********************
#include "config.h"
#include "board/config.h"
#include "drivers/led.h"
#include "drivers/pwm.h"
#include "drivers/usb.h"
#include "board/drivers/led.h"
#include "board/drivers/pwm.h"
#include "board/drivers/usb.h"
#include "early_init.h"
#include "provision.h"
#include "board/early_init.h"
#include "board/provision.h"
#include "crypto/rsa.h"
#include "crypto/sha.h"
#include "obj/cert.h"
#include "obj/gitversion.h"
#include "flasher.h"
#include "board/obj/cert.h"
#include "board/obj/gitversion.h"
#include "board/flasher.h"
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck
void __initialize_hardware_early(void) {
@@ -32,9 +30,6 @@ void fail(void) {
// know where to sig check
extern void *_app_start[];
// FIXME: sometimes your panda will fail flashing and will quickly blink a single Green LED
// BOUNTY: $200 coupon on shop.comma.ai or $100 check.
int main(void) {
// Init interrupt table
init_interrupts(true);

View File

@@ -34,9 +34,9 @@
// platform includes
#ifdef STM32H7
#include "stm32h7/stm32h7_config.h"
#include "board/stm32h7/stm32h7_config.h"
#elif defined(STM32F4)
#include "stm32f4/stm32f4_config.h"
#include "board/stm32f4/stm32f4_config.h"
#else
// TODO: uncomment this, cppcheck complains
// building for tests

View File

@@ -1,6 +1,5 @@
#pragma once
#if defined(ENABLE_SPI) || defined(BOOTSTUB)
uint8_t crc_checksum(const uint8_t *dat, int len, const uint8_t poly) {
uint8_t crc = 0xFFU;
int i;
@@ -18,4 +17,3 @@ uint8_t crc_checksum(const uint8_t *dat, int len, const uint8_t poly) {
}
return crc;
}
#endif

View File

@@ -139,9 +139,9 @@ bus_config_t bus_config[BUS_CONFIG_ARRAY_SIZE] = {
void can_init_all(void) {
for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) {
if (!current_board->has_canfd) {
#ifndef CANFD
bus_config[i].can_data_speed = 0U;
}
#endif
can_clear(can_queues[i]);
(void)can_init(i);
}
@@ -160,28 +160,28 @@ void can_set_forwarding(uint8_t from, uint8_t to) {
}
#endif
void ignition_can_hook(CANPacket_t *to_push) {
int bus = GET_BUS(to_push);
void ignition_can_hook(CANPacket_t *msg) {
int bus = GET_BUS(msg);
if (bus == 0) {
int addr = GET_ADDR(to_push);
int len = GET_LEN(to_push);
int addr = GET_ADDR(msg);
int len = GET_LEN(msg);
// GM exception
if ((addr == 0x1F1) && (len == 8)) {
// SystemPowerMode (2=Run, 3=Crank Request)
ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U;
ignition_can = (msg->data[0] & 0x2U) != 0U;
ignition_can_cnt = 0U;
}
// Rivian R1S/T GEN1 exception
if ((addr == 0x152) && (len == 8)) {
// 0x152 overlaps with Subaru pre-global which has this bit as the high beam
int counter = GET_BYTE(to_push, 1) & 0xFU; // max is only 14
int counter = msg->data[1] & 0xFU; // max is only 14
static int prev_counter_rivian = -1;
if ((counter == ((prev_counter_rivian + 1) % 15)) && (prev_counter_rivian != -1)) {
// VDM_OutputSignals->VDM_EpasPowerMode
ignition_can = ((GET_BYTE(to_push, 7) >> 4U) & 0x3U) == 1U; // VDM_EpasPowerMode_Drive_On=1
ignition_can = ((msg->data[7] >> 4U) & 0x3U) == 1U; // VDM_EpasPowerMode_Drive_On=1
ignition_can_cnt = 0U;
}
prev_counter_rivian = counter;
@@ -190,12 +190,12 @@ void ignition_can_hook(CANPacket_t *to_push) {
// Tesla Model 3/Y exception
if ((addr == 0x221) && (len == 8)) {
// 0x221 overlaps with Rivian which has random data on byte 0
int counter = GET_BYTE(to_push, 6) >> 4;
int counter = msg->data[6] >> 4;
static int prev_counter_tesla = -1;
if ((counter == ((prev_counter_tesla + 1) % 16)) && (prev_counter_tesla != -1)) {
// VCFRONT_LVPowerState->VCFRONT_vehiclePowerState
int power_state = (GET_BYTE(to_push, 0) >> 5U) & 0x3U;
int power_state = (msg->data[0] >> 5U) & 0x3U;
ignition_can = power_state == 0x3; // VEHICLE_POWER_STATE_DRIVE=3
ignition_can_cnt = 0U;
}
@@ -204,7 +204,7 @@ void ignition_can_hook(CANPacket_t *to_push) {
// Mazda exception
if ((addr == 0x9E) && (len == 8)) {
ignition_can = (GET_BYTE(to_push, 0) >> 5) == 0x6U;
ignition_can = (msg->data[0] >> 5) == 0x6U;
ignition_can_cnt = 0U;
}

View File

@@ -1,4 +1,4 @@
#include "stm32h7/lli2c.h"
#include "board/stm32h7/lli2c.h"
#define CODEC_I2C_ADDR 0x10

View File

@@ -4,31 +4,29 @@ struct harness_t harness;
// The ignition relay is only used for testing purposes
void set_intercept_relay(bool intercept, bool ignition_relay) {
if (current_board->harness_config->has_harness) {
bool drive_relay = intercept;
if (harness.status == HARNESS_STATUS_NC) {
// no harness, no relay to drive
drive_relay = false;
}
bool drive_relay = intercept;
if (harness.status == HARNESS_STATUS_NC) {
// no harness, no relay to drive
drive_relay = false;
}
if (drive_relay || ignition_relay) {
harness.relay_driven = true;
}
if (drive_relay || ignition_relay) {
harness.relay_driven = true;
}
// wait until we're not reading the analog voltages anymore
while (harness.sbu_adc_lock) {}
// wait until we're not reading the analog voltages anymore
while (harness.sbu_adc_lock) {}
if (harness.status == HARNESS_STATUS_NORMAL) {
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay);
set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay);
} else {
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay);
set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay);
}
if (harness.status == HARNESS_STATUS_NORMAL) {
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay);
set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay);
} else {
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay);
set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay);
}
if (!(drive_relay || ignition_relay)) {
harness.relay_driven = false;
}
if (!(drive_relay || ignition_relay)) {
harness.relay_driven = false;
}
}
@@ -56,13 +54,13 @@ static uint8_t harness_detect_orientation(void) {
#ifndef BOOTSTUB
// We can't detect orientation if the relay is being driven
if (!harness.relay_driven && current_board->harness_config->has_harness) {
if (!harness.relay_driven) {
harness.sbu_adc_lock = true;
set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_ANALOG);
set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_ANALOG);
harness.sbu1_voltage_mV = adc_get_mV(current_board->harness_config->adc_channel_SBU1);
harness.sbu2_voltage_mV = adc_get_mV(current_board->harness_config->adc_channel_SBU2);
harness.sbu1_voltage_mV = adc_get_mV(&current_board->harness_config->adc_signal_SBU1);
harness.sbu2_voltage_mV = adc_get_mV(&current_board->harness_config->adc_signal_SBU2);
uint16_t detection_threshold = current_board->avdd_mV / 2U;
// Detect connection and orientation

View File

@@ -14,7 +14,6 @@ struct harness_t {
extern struct harness_t harness;
struct harness_configuration {
const bool has_harness;
GPIO_TypeDef * const GPIO_SBU1;
GPIO_TypeDef * const GPIO_SBU2;
GPIO_TypeDef * const GPIO_relay_SBU1;
@@ -23,8 +22,8 @@ struct harness_configuration {
const uint8_t pin_SBU2;
const uint8_t pin_relay_SBU1;
const uint8_t pin_relay_SBU2;
const uint8_t adc_channel_SBU1;
const uint8_t adc_channel_SBU2;
const adc_signal_t adc_signal_SBU1;
const adc_signal_t adc_signal_SBU2;
};
// The ignition relay is only used for testing purposes

View File

@@ -3,54 +3,54 @@
// TODO: Implement for 32-bit timers
void pwm_init(TIM_TypeDef *TIM, uint8_t channel){
// Enable timer and auto-reload
register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU);
// Enable timer and auto-reload
register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU);
// Set channel as PWM mode 1 and enable output
switch(channel){
case 1U:
register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC1E);
break;
case 2U:
register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC2E);
break;
case 3U:
register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC3E);
break;
case 4U:
register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC4E);
break;
default:
break;
}
// Set channel as PWM mode 1 and enable output
switch(channel){
case 1U:
register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC1E);
break;
case 2U:
register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC2E);
break;
case 3U:
register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC3E);
break;
case 4U:
register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE));
register_set_bits(&(TIM->CCER), TIM_CCER_CC4E);
break;
default:
break;
}
// Set max counter value
register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU);
// Set max counter value
register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU);
// Update registers and clear counter
TIM->EGR |= TIM_EGR_UG;
// Update registers and clear counter
TIM->EGR |= TIM_EGR_UG;
}
void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){
uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U);
switch(channel){
case 1U:
register_set(&(TIM->CCR1), comp_value, 0xFFFFU);
break;
case 2U:
register_set(&(TIM->CCR2), comp_value, 0xFFFFU);
break;
case 3U:
register_set(&(TIM->CCR3), comp_value, 0xFFFFU);
break;
case 4U:
register_set(&(TIM->CCR4), comp_value, 0xFFFFU);
break;
default:
break;
}
uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U);
switch(channel){
case 1U:
register_set(&(TIM->CCR1), comp_value, 0xFFFFU);
break;
case 2U:
register_set(&(TIM->CCR2), comp_value, 0xFFFFU);
break;
case 3U:
register_set(&(TIM->CCR3), comp_value, 0xFFFFU);
break;
case 4U:
register_set(&(TIM->CCR4), comp_value, 0xFFFFU);
break;
default:
break;
}
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include "spi_declarations.h"
#include "crc.h"
#include "board/drivers/spi_declarations.h"
#include "board/crc.h"
#ifdef STM32H7
#define SPI_BUF_SIZE 2048U
@@ -14,9 +14,8 @@ uint8_t spi_buf_rx[SPI_BUF_SIZE];
uint8_t spi_buf_tx[SPI_BUF_SIZE];
#endif
uint16_t spi_checksum_error_count = 0;
uint16_t spi_error_count = 0;
#if defined(ENABLE_SPI) || defined(BOOTSTUB)
static uint8_t spi_state = SPI_STATE_HEADER;
static uint16_t spi_data_len_mosi;
static bool spi_can_tx_ready = false;
@@ -153,9 +152,12 @@ void spi_rx_done(void) {
print("SPI: did expect data for can_write\n");
}
} else if (spi_endpoint == 0xABU) {
// test endpoint, send max response length
// test endpoint: mimics panda -> device transfer
response_len = spi_data_len_miso;
response_ack = true;
} else if (spi_endpoint == 0xACU) {
// test endpoint: mimics device -> panda transfer (with NACK)
response_ack = false;
} else {
print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n");
}
@@ -206,8 +208,8 @@ void spi_rx_done(void) {
llspi_miso_dma(spi_buf_tx, response_len);
spi_state = next_rx_state;
if (!checksum_valid && (spi_checksum_error_count < UINT16_MAX)) {
spi_checksum_error_count += 1U;
if (!checksum_valid && (spi_error_count < UINT16_MAX)) {
spi_error_count += 1U;
}
}
@@ -234,8 +236,3 @@ void spi_tx_done(bool reset) {
void can_tx_comms_resume_spi(void) {
spi_can_tx_ready = true;
}
#else
void can_tx_comms_resume_spi(void) {
return;
}
#endif

View File

@@ -1,6 +1,6 @@
#pragma once
#include "crc.h"
#include "board/crc.h"
#define SPI_TIMEOUT_US 10000U
@@ -35,7 +35,7 @@ enum {
SPI_STATE_DATA_TX
};
extern uint16_t spi_checksum_error_count;
extern uint16_t spi_error_count;
#define SPI_HEADER_SIZE 7U
@@ -45,8 +45,6 @@ void llspi_mosi_dma(uint8_t *addr, int len);
void llspi_miso_dma(uint8_t *addr, int len);
void can_tx_comms_resume_spi(void);
#if defined(ENABLE_SPI) || defined(BOOTSTUB)
void spi_init(void);
void spi_rx_done(void);
void spi_tx_done(bool reset);
#endif

View File

@@ -1,7 +1,5 @@
#include "uart_declarations.h"
// IRQs: USART2, USART3, UART5
// ***************************** Definitions *****************************
#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, overwrite_mode) \
@@ -112,15 +110,6 @@ bool put_char(uart_ring *q, char elem) {
return ret;
}
void clear_uart_buff(uart_ring *q) {
ENTER_CRITICAL();
q->w_ptr_tx = 0;
q->r_ptr_tx = 0;
q->w_ptr_rx = 0;
q->r_ptr_rx = 0;
EXIT_CRITICAL();
}
// ************************ High-level debug functions **********************
void putch(const char a) {
// misra-c2012-17.7: serial debug function, ok to ignore output
@@ -145,14 +134,14 @@ void puth(unsigned int i) {
puthx(i, 8U);
}
#if defined(ENABLE_SPI) || defined(BOOTSTUB) || defined(DEBUG)
void puth4(unsigned int i) {
#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG)
static void puth4(unsigned int i) {
puthx(i, 4U);
}
#endif
#if defined(ENABLE_SPI) || defined(BOOTSTUB) || defined(DEBUG_USB) || defined(DEBUG_COMMS)
void hexdump(const void *a, int l) {
#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG_USB) || defined(DEBUG_COMMS)
static void hexdump(const void *a, int l) {
if (a != NULL) {
for (int i=0; i < l; i++) {
if ((i != 0) && ((i & 0xf) == 0)) print("\n");

View File

@@ -1,7 +1,5 @@
#pragma once
// IRQs: USART2, USART3, UART5
// ***************************** Definitions *****************************
#define FIFO_SIZE_INT 0x400U
@@ -33,7 +31,9 @@ void putch(const char a);
void print(const char *a);
void puthx(uint32_t i, uint8_t len);
void puth(unsigned int i);
#if defined(ENABLE_SPI) || defined(BOOTSTUB) || defined(DEBUG)
void puth4(unsigned int i);
#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG)
static void puth4(unsigned int i);
#endif
#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS)
static void hexdump(const void *a, int l);
#endif
void hexdump(const void *a, int l);

View File

@@ -57,9 +57,7 @@ void early_initialization(void) {
if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) {
led_init();
#ifdef PANDA
current_board->init_bootloader();
#endif
led_set(LED_GREEN, 1);
jump_to_bootloader();
}

View File

@@ -7,7 +7,6 @@
#define CANFD
#define ALLOW_DEBUG
#define PANDA
#define ENTER_CRITICAL() 0
#define EXIT_CRITICAL() 0

View File

@@ -134,15 +134,12 @@ void soft_flasher_start(void) {
gpio_usb_init();
led_init();
// enable USB
// enable comms
usb_init();
#ifdef ENABLE_SPI
if (current_board->has_spi) {
gpio_spi_init();
spi_init();
}
#endif
// green LED on for flashing
led_set(LED_GREEN, 1);

View File

@@ -23,7 +23,7 @@ struct __attribute__((packed)) health_t {
float interrupt_load_pkt;
uint8_t fan_power;
uint8_t safety_rx_checks_invalid_pkt;
uint16_t spi_checksum_error_count_pkt;
uint16_t spi_error_count_pkt;
uint8_t fan_stall_count;
uint16_t sbu1_voltage_mV;
uint16_t sbu2_voltage_mV;

View File

@@ -1,18 +0,0 @@
import os
import copy
Import('build_project', 'base_project_f4', 'base_project_h7')
build_projects = {
"panda_jungle": base_project_f4,
"panda_jungle_h7": base_project_h7,
}
for project_name, project in build_projects.items():
flags = [
"-DPANDA_JUNGLE",
]
if os.getenv("FINAL_PROVISIONING"):
flags += ["-DFINAL_PROVISIONING"]
build_project(project_name, project, flags)

View File

@@ -7,7 +7,7 @@ from panda import Panda, PandaDFU
from panda.python.constants import McuType
BASEDIR = os.path.dirname(os.path.realpath(__file__))
FW_PATH = os.path.join(BASEDIR, "obj/")
FW_PATH = os.path.join(BASEDIR, "../obj/")
def ensure_jungle_health_packet_version(fn):

View File

@@ -2,6 +2,7 @@
typedef void (*board_init)(void);
typedef void (*board_board_tick)(void);
typedef bool (*board_get_button)(void);
typedef void (*board_init_bootloader)(void);
typedef void (*board_set_panda_power)(bool enabled);
typedef void (*board_set_panda_individual_power)(uint8_t port_num, bool enabled);
typedef void (*board_set_ignition)(bool enabled);
@@ -17,12 +18,11 @@ struct board {
GPIO_TypeDef * const led_GPIO[3];
const uint8_t led_pin[3];
const uint8_t led_pwm_channels[3]; // leave at 0 to disable PWM
const bool has_canfd;
const bool has_sbu_sense;
const uint16_t avdd_mV;
board_init init;
board_board_tick board_tick;
board_get_button get_button;
board_init_bootloader init_bootloader;
board_set_panda_power set_panda_power;
board_set_panda_individual_power set_panda_individual_power;
board_set_ignition set_ignition;
@@ -40,7 +40,6 @@ struct board {
// ******************* Definitions ********************
#define HW_TYPE_UNKNOWN 0U
#define HW_TYPE_V1 1U
#define HW_TYPE_V2 2U
// CAN modes
@@ -59,18 +58,3 @@ struct board {
uint8_t harness_orientation = HARNESS_ORIENTATION_NONE;
uint8_t can_mode = CAN_MODE_NORMAL;
uint8_t ignition = 0U;
void unused_set_individual_ignition(uint8_t bitmask) {
UNUSED(bitmask);
}
void unused_board_enable_header_pin(uint8_t pin_num, bool enabled) {
UNUSED(pin_num);
UNUSED(enabled);
}
void unused_set_panda_individual_power(uint8_t port_num, bool enabled) {
UNUSED(port_num);
UNUSED(enabled);
}

View File

@@ -1,158 +0,0 @@
// ///////////////////////// //
// Jungle board v1 (STM32F4) //
// ///////////////////////// //
void board_v1_enable_can_transceiver(uint8_t transceiver, bool enabled) {
switch (transceiver) {
case 1U:
set_gpio_output(GPIOC, 1, !enabled);
break;
case 2U:
set_gpio_output(GPIOC, 13, !enabled);
break;
case 3U:
set_gpio_output(GPIOA, 0, !enabled);
break;
case 4U:
set_gpio_output(GPIOB, 10, !enabled);
break;
default:
print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n");
break;
}
}
void board_v1_set_can_mode(uint8_t mode) {
board_v1_enable_can_transceiver(2U, false);
board_v1_enable_can_transceiver(4U, false);
switch (mode) {
case CAN_MODE_NORMAL:
print("Setting normal CAN mode\n");
// B12,B13: disable OBD mode
set_gpio_mode(GPIOB, 12, MODE_INPUT);
set_gpio_mode(GPIOB, 13, MODE_INPUT);
// B5,B6: normal CAN2 mode
set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2);
set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2);
can_mode = CAN_MODE_NORMAL;
board_v1_enable_can_transceiver(2U, true);
break;
case CAN_MODE_OBD_CAN2:
print("Setting OBD CAN mode\n");
// B5,B6: disable normal CAN2 mode
set_gpio_mode(GPIOB, 5, MODE_INPUT);
set_gpio_mode(GPIOB, 6, MODE_INPUT);
// B12,B13: OBD mode
set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2);
set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2);
can_mode = CAN_MODE_OBD_CAN2;
board_v1_enable_can_transceiver(4U, true);
break;
default:
print("Tried to set unsupported CAN mode: "); puth(mode); print("\n");
break;
}
}
void board_v1_set_harness_orientation(uint8_t orientation) {
switch (orientation) {
case HARNESS_ORIENTATION_NONE:
set_gpio_output(GPIOA, 2, false);
set_gpio_output(GPIOA, 3, false);
set_gpio_output(GPIOA, 4, false);
set_gpio_output(GPIOA, 5, false);
harness_orientation = orientation;
break;
case HARNESS_ORIENTATION_1:
set_gpio_output(GPIOA, 2, false);
set_gpio_output(GPIOA, 3, (ignition != 0U));
set_gpio_output(GPIOA, 4, true);
set_gpio_output(GPIOA, 5, false);
harness_orientation = orientation;
break;
case HARNESS_ORIENTATION_2:
set_gpio_output(GPIOA, 2, (ignition != 0U));
set_gpio_output(GPIOA, 3, false);
set_gpio_output(GPIOA, 4, false);
set_gpio_output(GPIOA, 5, true);
harness_orientation = orientation;
break;
default:
print("Tried to set an unsupported harness orientation: "); puth(orientation); print("\n");
break;
}
}
bool panda_power = false;
void board_v1_set_panda_power(bool enable) {
panda_power = enable;
set_gpio_output(GPIOB, 14, enable);
}
bool board_v1_get_button(void) {
return get_gpio_input(GPIOC, 8);
}
void board_v1_set_ignition(bool enabled) {
ignition = enabled ? 0xFFU : 0U;
board_v1_set_harness_orientation(harness_orientation);
}
float board_v1_get_channel_power(uint8_t channel) {
UNUSED(channel);
return 0.0f;
}
uint16_t board_v1_get_sbu_mV(uint8_t channel, uint8_t sbu) {
UNUSED(channel); UNUSED(sbu);
return 0U;
}
void board_v1_init(void) {
common_init_gpio();
// A8,A15: normal CAN3 mode
set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3);
set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3);
board_v1_set_can_mode(CAN_MODE_NORMAL);
// Enable CAN transceivers
for(uint8_t i = 1; i <= 4; i++) {
board_v1_enable_can_transceiver(i, true);
}
// Set normal CAN mode
board_v1_set_can_mode(CAN_MODE_NORMAL);
// Set to no harness orientation
board_v1_set_harness_orientation(HARNESS_ORIENTATION_NONE);
// Enable panda power by default
board_v1_set_panda_power(true);
}
void board_v1_tick(void) {}
board board_v1 = {
.has_canfd = false,
.has_sbu_sense = false,
.avdd_mV = 3300U,
.init = &board_v1_init,
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {9, 7, 6},
.board_tick = &board_v1_tick,
.get_button = &board_v1_get_button,
.set_panda_power = &board_v1_set_panda_power,
.set_panda_individual_power = &unused_set_panda_individual_power,
.set_ignition = &board_v1_set_ignition,
.set_individual_ignition = &unused_set_individual_ignition,
.set_harness_orientation = &board_v1_set_harness_orientation,
.set_can_mode = &board_v1_set_can_mode,
.enable_can_transceiver = &board_v1_enable_can_transceiver,
.enable_header_pin = &unused_board_enable_header_pin,
.get_channel_power = &board_v1_get_channel_power,
.get_sbu_mV = &board_v1_get_sbu_mV,
};

View File

@@ -2,6 +2,8 @@
// Jungle board v2 (STM32H7) //
// ///////////////////////// //
#define ADC_CHANNEL(a, c) {.adc = (a), .channel = (c), .sample_time = SAMPLETIME_810_CYCLES, .oversampling = OVERSAMPLING_1}
gpio_t power_pins[] = {
{.bank = GPIOA, .pin = 0},
{.bank = GPIOA, .pin = 1},
@@ -47,22 +49,22 @@ gpio_t sbu2_relay_pins[] = {
{.bank = GPIOE, .pin = 12},
};
adc_channel_t sbu1_channels[] = {
{.adc = ADC3, .channel = 12},
{.adc = ADC3, .channel = 2},
{.adc = ADC3, .channel = 4},
{.adc = ADC3, .channel = 6},
{.adc = ADC3, .channel = 8},
{.adc = ADC3, .channel = 10},
const adc_signal_t sbu1_channels[] = {
ADC_CHANNEL(ADC3, 12),
ADC_CHANNEL(ADC3, 2),
ADC_CHANNEL(ADC3, 4),
ADC_CHANNEL(ADC3, 6),
ADC_CHANNEL(ADC3, 8),
ADC_CHANNEL(ADC3, 10),
};
adc_channel_t sbu2_channels[] = {
{.adc = ADC1, .channel = 13},
{.adc = ADC3, .channel = 3},
{.adc = ADC3, .channel = 5},
{.adc = ADC3, .channel = 7},
{.adc = ADC3, .channel = 9},
{.adc = ADC3, .channel = 11},
const adc_signal_t sbu2_channels[] = {
ADC_CHANNEL(ADC1, 13),
ADC_CHANNEL(ADC3, 3),
ADC_CHANNEL(ADC3, 5),
ADC_CHANNEL(ADC3, 7),
ADC_CHANNEL(ADC3, 9),
ADC_CHANNEL(ADC3, 11),
};
void board_v2_set_harness_orientation(uint8_t orientation) {
@@ -204,8 +206,7 @@ void board_v2_set_individual_ignition(uint8_t bitmask) {
float board_v2_get_channel_power(uint8_t channel) {
float ret = 0.0f;
if ((channel >= 1U) && (channel <= 6U)) {
uint16_t readout = adc_get_mV(ADC1, channel - 1U); // these are mapped nicely in hardware
uint16_t readout = adc_get_mV(&(const adc_signal_t) ADC_CHANNEL(ADC1, channel - 1U)); // these are mapped nicely in hardware
ret = (((float) readout / 33e6) - 0.8e-6) / 52e-6 * 12.0f;
} else {
print("Invalid channel ("); puth(channel); print(")\n");
@@ -218,10 +219,10 @@ uint16_t board_v2_get_sbu_mV(uint8_t channel, uint8_t sbu) {
if ((channel >= 1U) && (channel <= 6U)) {
switch(sbu){
case SBU1:
ret = adc_get_mV(sbu1_channels[channel - 1U].adc, sbu1_channels[channel - 1U].channel);
ret = adc_get_mV(&sbu1_channels[channel - 1U]);
break;
case SBU2:
ret = adc_get_mV(sbu2_channels[channel - 1U].adc, sbu2_channels[channel - 1U].channel);
ret = adc_get_mV(&sbu2_channels[channel - 1U]);
break;
default:
print("Invalid SBU ("); puth(sbu); print(")\n");
@@ -287,10 +288,9 @@ void board_v2_init(void) {
void board_v2_tick(void) {}
board board_v2 = {
.has_canfd = true,
.has_sbu_sense = true,
.avdd_mV = 3300U,
.init = &board_v2_init,
.init_bootloader = &board_v2_tick,
.led_GPIO = {GPIOE, GPIOE, GPIOE},
.led_pin = {4, 3, 2},
.board_tick = &board_v2_tick,

View File

@@ -11,20 +11,16 @@
#include "board/provision.h"
#include "board/health.h"
#include "jungle_health.h"
#include "board/jungle/jungle_health.h"
#include "board/drivers/can_common.h"
#ifdef STM32H7
#include "board/drivers/fdcan.h"
#else
#include "board/drivers/bxcan.h"
#endif
#include "board/drivers/fdcan.h"
#include "board/obj/gitversion.h"
#include "board/can_comms.h"
#include "main_comms.h"
#include "board/jungle/main_comms.h"
// ********************* Serial debugging *********************
@@ -106,17 +102,15 @@ void tick_handler(void) {
current_board->set_individual_ignition(ignition_bitmask);
// SBU voltage reporting
if (current_board->has_sbu_sense) {
for (uint8_t i = 0U; i < 6U; i++) {
CANPacket_t pkt = { 0 };
pkt.data_len_code = 8U;
pkt.addr = 0x100U + i;
*(uint16_t *) &pkt.data[0] = current_board->get_sbu_mV(i + 1U, SBU1);
*(uint16_t *) &pkt.data[2] = current_board->get_sbu_mV(i + 1U, SBU2);
pkt.data[4] = (ignition_bitmask >> i) & 1U;
can_set_checksum(&pkt);
can_send(&pkt, 0U, false);
}
for (uint8_t i = 0U; i < 6U; i++) {
CANPacket_t pkt = { 0 };
pkt.data_len_code = 8U;
pkt.addr = 0x100U + i;
*(uint16_t *) &pkt.data[0] = current_board->get_sbu_mV(i + 1U, SBU1);
*(uint16_t *) &pkt.data[2] = current_board->get_sbu_mV(i + 1U, SBU2);
pkt.data[4] = (ignition_bitmask >> i) & 1U;
can_set_checksum(&pkt);
can_send(&pkt, 0U, false);
}
#else
// toggle ignition on button press

View File

@@ -219,11 +219,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
print("Clearing CAN CAN ring buffer failed: wrong bus number\n");
}
break;
// **** 0xf2: Clear debug ring buffer.
case 0xf2:
print("Clearing debug queue.\n");
clear_uart_buff(get_ring_by_number(0));
break;
// **** 0xf4: Set CAN transceiver enable pin
case 0xf4:
current_board->enable_can_transceiver(req->param1, req->param2 > 0U);
@@ -240,7 +235,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
// **** 0xf9: set CAN FD data bitrate
case 0xf9:
if ((req->param1 < PANDA_CAN_CNT) &&
current_board->has_canfd &&
is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) {
bus_config[req->param1].can_data_speed = req->param2;
bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed);
@@ -251,7 +245,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break;
// **** 0xfc: set CAN FD non-ISO mode
case 0xfc:
if ((req->param1 < PANDA_CAN_CNT) && current_board->has_canfd) {
if (req->param1 < PANDA_CAN_CNT) {
bus_config[req->param1].canfd_non_iso = (req->param2 != 0U);
bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1));
UNUSED(ret);

View File

@@ -1,7 +0,0 @@
#include "boards/board_declarations.h"
#include "boards/board_v1.h"
void detect_board_type(void) {
hw_type = HW_TYPE_V1;
current_board = &board_v1;
}

View File

@@ -1,7 +1,7 @@
#include "boards/board_declarations.h"
#include "board/jungle/boards/board_declarations.h"
#include "stm32h7/lladc.h"
#include "boards/board_v2.h"
#include "board/stm32h7/lladc.h"
#include "board/jungle/boards/board_v2.h"
void detect_board_type(void) {
hw_type = HW_TYPE_V2;

View File

@@ -1,54 +0,0 @@
typedef struct {
ADC_TypeDef *adc;
uint8_t channel;
} adc_channel_t;
void adc_init(ADC_TypeDef *adc) {
adc->CR &= ~(ADC_CR_DEEPPWD); // Reset deep-power-down mode
adc->CR |= ADC_CR_ADVREGEN; // Enable ADC regulator
while(!(adc->ISR & ADC_ISR_LDORDY) && (adc != ADC3));
if (adc != ADC3) {
adc->CR &= ~(ADC_CR_ADCALDIF); // Choose single-ended calibration
adc->CR |= ADC_CR_ADCALLIN; // Lineriality calibration
}
adc->CR |= ADC_CR_ADCAL; // Start calibrtation
while((adc->CR & ADC_CR_ADCAL) != 0);
adc->ISR |= ADC_ISR_ADRDY;
adc->CR |= ADC_CR_ADEN;
while(!(adc->ISR & ADC_ISR_ADRDY));
}
uint16_t adc_get_raw(ADC_TypeDef *adc, uint8_t channel) {
adc->SQR1 &= ~(ADC_SQR1_L);
adc->SQR1 = ((uint32_t) channel << 6U);
if (channel < 10U) {
adc->SMPR1 = (0x7U << (channel * 3U));
} else {
adc->SMPR2 = (0x7U << ((channel - 10U) * 3U));
}
adc->PCSEL_RES0 = (0x1U << channel);
adc->CR |= ADC_CR_ADSTART;
while (!(adc->ISR & ADC_ISR_EOC));
uint16_t res = adc->DR;
while (!(adc->ISR & ADC_ISR_EOS));
adc->ISR |= ADC_ISR_EOS;
return res;
}
uint16_t adc_get_mV(ADC_TypeDef *adc, uint8_t channel) {
uint16_t ret = 0;
if ((adc == ADC1) || (adc == ADC2)) {
ret = (adc_get_raw(adc, channel) * current_board->avdd_mV) / 65535U;
} else if (adc == ADC3) {
ret = (adc_get_raw(adc, channel) * current_board->avdd_mV) / 4095U;
} else {}
return ret;
}

View File

@@ -1,60 +1,41 @@
// ********************* Includes *********************
#include "config.h"
#include "board/config.h"
#include "drivers/led.h"
#include "drivers/pwm.h"
#include "drivers/usb.h"
#include "drivers/simple_watchdog.h"
#include "drivers/bootkick.h"
#include "board/drivers/led.h"
#include "board/drivers/pwm.h"
#include "board/drivers/usb.h"
#include "board/drivers/simple_watchdog.h"
#include "board/drivers/bootkick.h"
#include "early_init.h"
#include "provision.h"
#include "board/early_init.h"
#include "board/provision.h"
#include "opendbc/safety/safety.h"
#include "health.h"
#include "board/health.h"
#include "drivers/can_common.h"
#include "board/drivers/can_common.h"
#ifdef STM32H7
#include "drivers/fdcan.h"
#include "board/drivers/fdcan.h"
#else
#include "drivers/bxcan.h"
#include "board/drivers/bxcan.h"
#endif
#include "power_saving.h"
#include "board/power_saving.h"
#include "obj/gitversion.h"
#include "board/obj/gitversion.h"
#include "can_comms.h"
#include "main_comms.h"
#include "board/can_comms.h"
#include "board/main_comms.h"
// ********************* Serial debugging *********************
static bool check_started(void) {
bool started = current_board->check_ignition() || ignition_can;
return started;
}
void debug_ring_callback(uart_ring *ring) {
char rcv;
while (get_char(ring, &rcv)) {
(void)put_char(ring, rcv); // misra-c2012-17.7: cast to void is ok: debug function
// only allow bootloader entry on debug builds
#ifdef ALLOW_DEBUG
// jump to DFU flash
if (rcv == 'z') {
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
NVIC_SystemReset();
}
#endif
// normal reset
if (rcv == 'x') {
NVIC_SystemReset();
}
}
}
@@ -77,31 +58,26 @@ void set_safety_mode(uint16_t mode, uint16_t param) {
switch (mode_copy) {
case SAFETY_SILENT:
set_intercept_relay(false, false);
if (current_board->harness_config->has_harness) {
current_board->set_can_mode(CAN_MODE_NORMAL);
}
current_board->set_can_mode(CAN_MODE_NORMAL);
can_silent = ALL_CAN_SILENT;
break;
case SAFETY_NOOUTPUT:
set_intercept_relay(false, false);
if (current_board->harness_config->has_harness) {
current_board->set_can_mode(CAN_MODE_NORMAL);
}
current_board->set_can_mode(CAN_MODE_NORMAL);
can_silent = ALL_CAN_LIVE;
break;
case SAFETY_ELM327:
set_intercept_relay(false, false);
heartbeat_counter = 0U;
heartbeat_lost = false;
if (current_board->harness_config->has_harness) {
// Clear any pending messages in the can core (i.e. sending while comma power is unplugged)
// TODO: rewrite using hardware queues rather than fifo to cancel specific messages
can_clear_send(CANIF_FROM_CAN_NUM(1), 1);
if (param == 0U) {
current_board->set_can_mode(CAN_MODE_OBD_CAN2);
} else {
current_board->set_can_mode(CAN_MODE_NORMAL);
}
// Clear any pending messages in the can core (i.e. sending while comma power is unplugged)
// TODO: rewrite using hardware queues rather than fifo to cancel specific messages
can_clear_send(CANIF_FROM_CAN_NUM(1), 1);
if (param == 0U) {
current_board->set_can_mode(CAN_MODE_OBD_CAN2);
} else {
current_board->set_can_mode(CAN_MODE_NORMAL);
}
can_silent = ALL_CAN_LIVE;
break;
@@ -109,9 +85,7 @@ void set_safety_mode(uint16_t mode, uint16_t param) {
set_intercept_relay(true, false);
heartbeat_counter = 0U;
heartbeat_lost = false;
if (current_board->harness_config->has_harness) {
current_board->set_can_mode(CAN_MODE_NORMAL);
}
current_board->set_can_mode(CAN_MODE_NORMAL);
can_silent = ALL_CAN_LIVE;
break;
}
@@ -199,7 +173,8 @@ static void tick_handler(void) {
const bool recent_heartbeat = heartbeat_counter == 0U;
// tick drivers at 1Hz
bootkick_tick(check_started(), recent_heartbeat);
bool started = harness_check_ignition() || ignition_can;
bootkick_tick(started, recent_heartbeat);
// increase heartbeat counter and cap it at the uint32 limit
if (heartbeat_counter < UINT32_MAX) {
@@ -237,7 +212,7 @@ static void tick_handler(void) {
if (!heartbeat_disabled) {
// if the heartbeat has been gone for a while, go to SILENT safety mode and enter power save
if (heartbeat_counter >= (check_started() ? HEARTBEAT_IGNITION_CNT_ON : HEARTBEAT_IGNITION_CNT_OFF)) {
if (heartbeat_counter >= (started ? HEARTBEAT_IGNITION_CNT_ON : HEARTBEAT_IGNITION_CNT_OFF)) {
print("device hasn't sent a heartbeat for 0x");
puth(heartbeat_counter);
print(" seconds. Safety is set to SILENT mode.\n");
@@ -310,7 +285,7 @@ int main(void) {
// red+green leds enabled until succesful USB/SPI init, as a debug indicator
led_set(LED_RED, true);
led_set(LED_GREEN, true);
adc_init();
adc_init(ADC1);
// print hello
print("\n\n\n************************ MAIN START ************************\n");
@@ -324,9 +299,7 @@ int main(void) {
// init board
current_board->init();
current_board->set_can_mode(CAN_MODE_NORMAL);
if (current_board->harness_config->has_harness) {
harness_init();
}
harness_init();
// panda has an FPU, let's use it!
enable_fpu();
@@ -357,12 +330,10 @@ int main(void) {
// enable USB (right before interrupts or enum can fail!)
usb_init();
#ifdef ENABLE_SPI
if (current_board->has_spi) {
gpio_spi_init();
spi_init();
}
#endif
led_set(LED_RED, false);
led_set(LED_GREEN, false);

View File

@@ -12,8 +12,7 @@ static int get_health_pkt(void *dat) {
health->voltage_pkt = current_board->read_voltage_mV();
health->current_pkt = current_board->read_current_mA();
// Use the GPIO pin to determine ignition or use a CAN based logic
health->ignition_line_pkt = (uint8_t)(current_board->check_ignition());
health->ignition_line_pkt = (uint8_t)(harness_check_ignition());
health->ignition_can_pkt = ignition_can;
health->controls_allowed_pkt = controls_allowed;
@@ -29,7 +28,9 @@ static int get_health_pkt(void *dat) {
health->heartbeat_lost_pkt = heartbeat_lost;
health->safety_rx_checks_invalid_pkt = safety_rx_checks_invalid;
health->spi_checksum_error_count_pkt = spi_checksum_error_count;
#ifndef STM32F4
health->spi_error_count_pkt = spi_error_count;
#endif
health->fault_status_pkt = fault_status;
health->faults_pkt = faults;
@@ -97,7 +98,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
resp[1] = ((fan_state.rpm & 0xFF00U) >> 8U);
resp_len = 2;
break;
// **** 0xc0: reset communications
// **** 0xc0: reset communications state
case 0xc0:
comms_can_reset();
break;
@@ -212,17 +213,14 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break;
// **** 0xdb: set OBD CAN multiplexing mode
case 0xdb:
if (current_board->harness_config->has_harness) {
if (req->param1 == 1U) {
// Enable OBD CAN
current_board->set_can_mode(CAN_MODE_OBD_CAN2);
} else {
// Disable OBD CAN
current_board->set_can_mode(CAN_MODE_NORMAL);
}
if (req->param1 == 1U) {
// Enable OBD CAN
current_board->set_can_mode(CAN_MODE_OBD_CAN2);
} else {
// Disable OBD CAN
current_board->set_can_mode(CAN_MODE_NORMAL);
}
break;
// **** 0xdc: set safety mode
case 0xdc:
set_safety_mode(req->param1, (uint16_t)req->param2);
@@ -265,47 +263,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
++resp_len;
}
break;
// **** 0xe1: uart set baud rate
case 0xe1:
ur = get_ring_by_number(req->param1);
if (!ur) {
break;
}
uart_set_baud(ur->uart, req->param2);
break;
// **** 0xe2: uart set parity
case 0xe2:
ur = get_ring_by_number(req->param1);
if (!ur) {
break;
}
switch (req->param2) {
case 0:
// disable parity, 8-bit
ur->uart->CR1 &= ~(USART_CR1_PCE | USART_CR1_M);
break;
case 1:
// even parity, 9-bit
ur->uart->CR1 &= ~USART_CR1_PS;
ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M;
break;
case 2:
// odd parity, 9-bit
ur->uart->CR1 |= USART_CR1_PS;
ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M;
break;
default:
break;
}
break;
// **** 0xe4: uart set baud rate extended
case 0xe4:
ur = get_ring_by_number(req->param1);
if (!ur) {
break;
}
uart_set_baud(ur->uart, (int)req->param2*300);
break;
// **** 0xe5: set CAN loopback (for testing)
case 0xe5:
can_loopback = req->param1 > 0U;
@@ -335,16 +292,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
print("Clearing CAN CAN ring buffer failed: wrong bus number\n");
}
break;
// **** 0xf2: Clear UART ring buffer.
case 0xf2:
{
uart_ring * rb = get_ring_by_number(req->param1);
if (rb != NULL) {
print("Clearing UART queue.\n");
clear_uart_buff(rb);
}
break;
}
// **** 0xf3: Heartbeat. Resets heartbeat counter.
case 0xf3:
{
@@ -372,7 +319,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
// **** 0xf9: set CAN FD data bitrate
case 0xf9:
if ((req->param1 < PANDA_CAN_CNT) &&
current_board->has_canfd &&
is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) {
bus_config[req->param1].can_data_speed = req->param2;
bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed);
@@ -383,7 +329,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break;
// **** 0xfc: set CAN FD non-ISO mode
case 0xfc:
if ((req->param1 < PANDA_CAN_CNT) && current_board->has_canfd) {
if (req->param1 < PANDA_CAN_CNT) {
bus_config[req->param1].canfd_non_iso = (req->param2 != 0U);
bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1));
UNUSED(ret);

View File

@@ -3,8 +3,6 @@
// ******************** Prototypes ********************
void print(const char *a);
void puth(unsigned int i);
void puth4(unsigned int i);
void hexdump(const void *a, int l);
typedef struct board board;
typedef struct harness_configuration harness_configuration;
void pwm_init(TIM_TypeDef *TIM, uint8_t channel);

View File

@@ -7,7 +7,7 @@ int power_save_status = POWER_SAVE_STATUS_DISABLED;
void enable_can_transceivers(bool enabled) {
// Leave main CAN always on for CAN-based ignition detection
uint8_t main_bus = (current_board->harness_config->has_harness && (harness.status == HARNESS_STATUS_FLIPPED)) ? 3U : 1U;
uint8_t main_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
for(uint8_t i=1U; i<=4U; i++){
current_board->enable_can_transceiver(i, (i == main_bus) || enabled);
}

View File

@@ -1,39 +1,23 @@
// ///////////////////////////////////////////////////////////// //
// Hardware abstraction layer for all different supported boards //
// ///////////////////////////////////////////////////////////// //
#include "boards/board_declarations.h"
#include "boards/unused_funcs.h"
#include "board/boards/board_declarations.h"
#include "board/boards/unused_funcs.h"
// ///// Board definition and detection ///// //
#include "stm32f4/lladc.h"
#include "drivers/harness.h"
#include "drivers/fan.h"
#include "stm32f4/llfan.h"
#include "drivers/clock_source.h"
#include "boards/white.h"
#include "boards/black.h"
#include "boards/dos.h"
// Unused functions on F4
void sound_tick(void) {}
#include "board/stm32f4/lladc.h"
#include "board/drivers/harness.h"
#include "board/drivers/fan.h"
#include "board/stm32f4/llfan.h"
#include "board/drivers/clock_source.h"
#include "board/boards/dos.h"
void detect_board_type(void) {
// SPI lines floating: white (TODO: is this reliable? Not really, we have to enable ESP/GPS to be able to detect this on the UART)
set_gpio_output(GPIOC, 14, 1);
set_gpio_output(GPIOC, 5, 1);
if(!detect_with_pull(GPIOB, 1, PULL_UP) && !detect_with_pull(GPIOB, 7, PULL_UP)){
if (!detect_with_pull(GPIOB, 1, PULL_UP) && !detect_with_pull(GPIOB, 7, PULL_UP)) {
hw_type = HW_TYPE_DOS;
current_board = &board_dos;
} else if((detect_with_pull(GPIOA, 4, PULL_DOWN)) || (detect_with_pull(GPIOA, 5, PULL_DOWN)) || (detect_with_pull(GPIOA, 6, PULL_DOWN)) || (detect_with_pull(GPIOA, 7, PULL_DOWN))){
hw_type = HW_TYPE_WHITE_PANDA;
current_board = &board_white;
} else if(detect_with_pull(GPIOA, 13, PULL_DOWN)) { // Rev AB deprecated, so no pullup means black. In REV C, A13 is pulled up to 5V with a 10K
// grey is deprecated
} else if(!detect_with_pull(GPIOB, 15, PULL_UP)) {
// uno is deprecated
} else {
hw_type = HW_TYPE_BLACK_PANDA;
current_board = &board_black;
}
// Return A13 to the alt mode to fix SWD

View File

@@ -1,24 +1,31 @@
#include "lladc_declarations.h"
void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask);
void adc_init(void) {
void adc_init(ADC_TypeDef *adc) {
register_set(&(ADC->CCR), ADC_CCR_TSVREFE | ADC_CCR_VBATE, 0xC30000U);
register_set(&(ADC1->CR2), ADC_CR2_ADON, 0xFF7F0F03U);
register_set(&(ADC1->SMPR1), ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13, 0x7FFFFFFU);
register_set(&(adc->CR2), ADC_CR2_ADON, 0xFF7F0F03U);
}
uint16_t adc_get_raw(uint8_t channel) {
// Select channel
register_set(&(ADC1->JSQR), ((uint32_t) channel << 15U), 0x3FFFFFU);
static uint16_t adc_get_raw(const adc_signal_t *signal) {
// sample time
if (signal->channel < 10U) {
signal->adc->SMPR2 = ((uint32_t) signal->sample_time << (signal->channel * 3U));
} else {
signal->adc->SMPR1 = ((uint32_t) signal->sample_time << ((signal->channel - 10U) * 3U));
}
// Start conversion
ADC1->SR &= ~(ADC_SR_JEOC);
ADC1->CR2 |= ADC_CR2_JSWSTART;
while (!(ADC1->SR & ADC_SR_JEOC));
// select channel
signal->adc->JSQR = ((uint32_t) signal->channel << 15U);
return ADC1->JDR1;
// start conversion
signal->adc->SR &= ~(ADC_SR_JEOC);
signal->adc->CR2 |= ADC_CR2_JSWSTART;
while (!(signal->adc->SR & ADC_SR_JEOC));
return signal->adc->JDR1;
}
uint16_t adc_get_mV(uint8_t channel) {
return (adc_get_raw(channel) * current_board->avdd_mV) / 4095U;
uint16_t adc_get_mV(const adc_signal_t *signal) {
return (adc_get_raw(signal) * current_board->avdd_mV) / 4095U;
}

View File

@@ -0,0 +1,20 @@
#pragma once
typedef enum {
SAMPLETIME_3_CYCLES = 0,
SAMPLETIME_15_CYCLES = 1,
SAMPLETIME_28_CYCLES = 2,
SAMPLETIME_56_CYCLES = 3,
SAMPLETIME_84_CYCLES = 4,
SAMPLETIME_112_CYCLES = 5,
SAMPLETIME_144_CYCLES = 6,
SAMPLETIME_480_CYCLES = 7
} adc_sample_time_t;
typedef struct {
ADC_TypeDef *adc;
uint8_t channel;
adc_sample_time_t sample_time;
} adc_signal_t;
#define ADC_CHANNEL_DEFAULT(a, c) {.adc = (a), .channel = (c), .sample_time = SAMPLETIME_480_CYCLES}

View File

@@ -1,91 +1,12 @@
#if defined(ENABLE_SPI) || defined(BOOTSTUB)
void llspi_miso_dma(uint8_t *addr, int len) {
// disable DMA
DMA2_Stream3->CR &= ~DMA_SxCR_EN;
register_clear_bits(&(SPI1->CR2), SPI_CR2_TXDMAEN);
// setup source and length
register_set(&(DMA2_Stream3->M0AR), (uint32_t)addr, 0xFFFFFFFFU);
DMA2_Stream3->NDTR = len;
// enable DMA
register_set_bits(&(SPI1->CR2), SPI_CR2_TXDMAEN);
DMA2_Stream3->CR |= DMA_SxCR_EN;
UNUSED(addr);
UNUSED(len);
}
void llspi_mosi_dma(uint8_t *addr, int len) {
// disable DMA
register_clear_bits(&(SPI1->CR2), SPI_CR2_RXDMAEN);
DMA2_Stream2->CR &= ~DMA_SxCR_EN;
// drain the bus
volatile uint8_t dat = SPI1->DR;
(void)dat;
// setup destination and length
register_set(&(DMA2_Stream2->M0AR), (uint32_t)addr, 0xFFFFFFFFU);
DMA2_Stream2->NDTR = len;
// enable DMA
DMA2_Stream2->CR |= DMA_SxCR_EN;
register_set_bits(&(SPI1->CR2), SPI_CR2_RXDMAEN);
}
// SPI MOSI DMA FINISHED
static void DMA2_Stream2_IRQ_Handler(void) {
// Clear interrupt flag
ENTER_CRITICAL();
DMA2->LIFCR = DMA_LIFCR_CTCIF2;
spi_rx_done();
EXIT_CRITICAL();
UNUSED(addr);
UNUSED(len);
}
// SPI MISO DMA FINISHED
static void DMA2_Stream3_IRQ_Handler(void) {
// Clear interrupt flag
DMA2->LIFCR = DMA_LIFCR_CTCIF3;
// Wait until the transaction is actually finished and clear the DR
// Timeout to prevent hang when the master clock stops.
bool timed_out = false;
uint32_t start_time = microsecond_timer_get();
while (!(SPI1->SR & SPI_SR_TXE)) {
if (get_ts_elapsed(microsecond_timer_get(), start_time) > SPI_TIMEOUT_US) {
timed_out = true;
break;
}
}
volatile uint8_t dat = SPI1->DR;
(void)dat;
SPI1->DR = 0U;
if (timed_out) {
print("SPI: TX timeout\n");
}
spi_tx_done(timed_out);
}
// ***************************** SPI init *****************************
void llspi_init(void) {
REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA)
REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA)
// Setup MOSI DMA
register_set(&(DMA2_Stream2->CR), (DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_TCIE), 0x1E077EFEU);
register_set(&(DMA2_Stream2->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU);
// Setup MISO DMA
register_set(&(DMA2_Stream3->CR), (DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE), 0x1E077EFEU);
register_set(&(DMA2_Stream3->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU);
// Enable SPI and the error interrupts
// TODO: verify clock phase and polarity
register_set(&(SPI1->CR1), SPI_CR1_SPE, 0xFFFFU);
register_set(&(SPI1->CR2), 0U, 0xF7U);
NVIC_EnableIRQ(DMA2_Stream2_IRQn);
NVIC_EnableIRQ(DMA2_Stream3_IRQn);
}
#endif

View File

@@ -29,7 +29,3 @@ void uart_tx_ring(uart_ring *q){
#define DIVMANT_(_PCLK_, _BAUD_) (DIV_((_PCLK_), (_BAUD_)) / 100U)
#define DIVFRAQ_(_PCLK_, _BAUD_) ((((DIV_((_PCLK_), (_BAUD_)) - (DIVMANT_((_PCLK_), (_BAUD_)) * 100U)) * 16U) + 50U) / 100U)
#define USART_BRR_(_PCLK_, _BAUD_) ((DIVMANT_((_PCLK_), (_BAUD_)) << 4) | (DIVFRAQ_((_PCLK_), (_BAUD_)) & 0x0FU))
void uart_set_baud(USART_TypeDef *u, unsigned int baud) {
u->BRR = USART_BRR_(APB1_FREQ*1000000U, baud);
}

View File

@@ -9,16 +9,8 @@ static void gpio_usb_init(void) {
GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12;
}
#ifdef ENABLE_SPI
void gpio_spi_init(void) {
// A4-A7: SPI
set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1);
set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1);
set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1);
set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1);
register_set_bits(&(GPIOA->OSPEEDR), GPIO_OSPEEDER_OSPEEDR4 | GPIO_OSPEEDER_OSPEEDR5 | GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7);
}
#endif
#ifdef BOOTSTUB
void gpio_usart2_init(void) {
@@ -30,8 +22,6 @@ void gpio_usart2_init(void) {
// Common GPIO initialization
void common_init_gpio(void) {
// TODO: Is this block actually doing something???
// pull low to hold ESP in reset??
// enable OTG out tied to ground
GPIOA->ODR = 0;
GPIOB->ODR = 0;

View File

@@ -1,5 +1,5 @@
#include "stm32f4/inc/stm32f4xx.h"
#include "stm32f4/inc/stm32f4xx_hal_gpio_ex.h"
#include "stm32f4xx.h"
#include "stm32f4xx_hal_gpio_ex.h"
#define MCU_IDCODE 0x463U
#define CORE_FREQ 96U // in MHz
@@ -30,44 +30,46 @@
#define PROVISION_CHUNK_ADDRESS 0x1FFF79E0U
#define DEVICE_SERIAL_NUMBER_ADDRESS 0x1FFF79C0U
#include "can.h"
#include "comms_definitions.h"
#include "board/can.h"
#include "board/comms_definitions.h"
#ifndef BOOTSTUB
#include "main_definitions.h"
#include "board/main_definitions.h"
#else
#include "bootstub_declarations.h"
#include "board/bootstub_declarations.h"
#endif
#include "libc.h"
#include "critical.h"
#include "faults.h"
#include "utils.h"
#include "board/libc.h"
#include "board/critical.h"
#include "board/faults.h"
#include "board/utils.h"
#include "drivers/registers.h"
#include "drivers/interrupts.h"
#include "drivers/gpio.h"
#include "stm32f4/peripherals.h"
#include "stm32f4/interrupt_handlers.h"
#include "drivers/timers.h"
#include "stm32f4/board.h"
#include "stm32f4/clock.h"
#include "drivers/spi.h"
#include "stm32f4/llspi.h"
#include "board/drivers/registers.h"
#include "board/drivers/interrupts.h"
#include "board/drivers/gpio.h"
#include "board/stm32f4/peripherals.h"
#include "board/stm32f4/interrupt_handlers.h"
#include "board/drivers/timers.h"
#include "board/stm32f4/board.h"
#include "board/stm32f4/clock.h"
#if !defined(BOOTSTUB)
#include "drivers/uart.h"
#include "stm32f4/lluart.h"
#include "board/drivers/uart.h"
#include "board/stm32f4/lluart.h"
#endif
#ifdef BOOTSTUB
#include "stm32f4/llflash.h"
#include "board/stm32f4/llflash.h"
#else
#include "stm32f4/llbxcan.h"
#include "board/stm32f4/llbxcan.h"
#endif
#include "stm32f4/llusb.h"
#include "board/stm32f4/llusb.h"
// unused
void spi_init(void) {};
void sound_tick(void) {};
void can_tx_comms_resume_spi(void) {};
void early_gpio_float(void) {
RCC->AHB1ENR = RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;

View File

@@ -1,20 +1,20 @@
// ///////////////////////////////////////////////////////////// //
// Hardware abstraction layer for all different supported boards //
// ///////////////////////////////////////////////////////////// //
#include "boards/board_declarations.h"
#include "boards/unused_funcs.h"
#include "board/boards/board_declarations.h"
#include "board/boards/unused_funcs.h"
// ///// Board definition and detection ///// //
#include "stm32h7/lladc.h"
#include "drivers/harness.h"
#include "drivers/fan.h"
#include "stm32h7/llfan.h"
#include "drivers/fake_siren.h"
#include "stm32h7/sound.h"
#include "drivers/clock_source.h"
#include "boards/red.h"
#include "boards/tres.h"
#include "boards/cuatro.h"
#include "board/stm32h7/lladc.h"
#include "board/drivers/harness.h"
#include "board/drivers/fan.h"
#include "board/stm32h7/llfan.h"
#include "board/drivers/fake_siren.h"
#include "board/stm32h7/sound.h"
#include "board/drivers/clock_source.h"
#include "board/boards/red.h"
#include "board/boards/tres.h"
#include "board/boards/cuatro.h"
void detect_board_type(void) {

View File

@@ -1,39 +1,80 @@
#include "lladc_declarations.h"
void adc_init(void) {
ADC1->CR &= ~(ADC_CR_DEEPPWD); //Reset deep-power-down mode
ADC1->CR |= ADC_CR_ADVREGEN; // Enable ADC regulator
while(!(ADC1->ISR & ADC_ISR_LDORDY));
static uint32_t adc_avdd_mV = 0U;
ADC1->CR &= ~(ADC_CR_ADCALDIF); // Choose single-ended calibration
ADC1->CR |= ADC_CR_ADCALLIN; // Lineriality calibration
ADC1->CR |= ADC_CR_ADCAL; // Start calibrtation
while((ADC1->CR & ADC_CR_ADCAL) != 0U);
void adc_init(ADC_TypeDef *adc) {
adc->CR &= ~(ADC_CR_ADEN); // Disable ADC
adc->CR &= ~(ADC_CR_DEEPPWD); // Reset deep-power-down mode
adc->CR |= ADC_CR_ADVREGEN; // Enable ADC regulator
while(!(adc->ISR & ADC_ISR_LDORDY) && (adc != ADC3));
ADC1->ISR |= ADC_ISR_ADRDY;
ADC1->CR |= ADC_CR_ADEN;
while(!(ADC1->ISR & ADC_ISR_ADRDY));
if (adc != ADC3) {
adc->CR &= ~(ADC_CR_ADCALDIF); // Choose single-ended calibration
adc->CR |= ADC_CR_ADCALLIN; // Lineriality calibration
}
adc->CR |= ADC_CR_ADCAL; // Start calibration
while((adc->CR & ADC_CR_ADCAL) != 0U);
adc->ISR |= ADC_ISR_ADRDY;
adc->CR |= ADC_CR_ADEN;
while(!(adc->ISR & ADC_ISR_ADRDY));
}
static uint16_t adc_get_raw(uint8_t channel) {
uint16_t res = 0U;
ADC1->SQR1 &= ~(ADC_SQR1_L);
ADC1->SQR1 = (uint32_t)channel << 6U;
uint16_t adc_get_raw(const adc_signal_t *signal) {
signal->adc->SQR1 &= ~(ADC_SQR1_L);
signal->adc->SQR1 = ((uint32_t) signal->channel << 6U);
ADC1->SMPR1 = 0x4UL << (channel * 3UL);
ADC1->PCSEL_RES0 = (0x1UL << channel);
ADC1->CFGR2 = (63UL << ADC_CFGR2_OVSR_Pos) | (0x6U << ADC_CFGR2_OVSS_Pos) | ADC_CFGR2_ROVSE;
// sample time
if (signal->channel < 10U) {
signal->adc->SMPR1 = ((uint32_t) signal->sample_time << (signal->channel * 3U));
} else {
signal->adc->SMPR2 = ((uint32_t) signal->sample_time << ((signal->channel - 10U) * 3U));
}
ADC1->CR |= ADC_CR_ADSTART;
while (!(ADC1->ISR & ADC_ISR_EOC));
// select channel
signal->adc->PCSEL_RES0 = (0x1UL << signal->channel);
res = ADC1->DR;
// oversampling
signal->adc->CFGR2 = (((1U << (uint32_t) signal->oversampling) - 1U) << ADC_CFGR2_OVSR_Pos) | ((uint32_t) signal->oversampling << ADC_CFGR2_OVSS_Pos);
signal->adc->CFGR2 |= (signal->oversampling != OVERSAMPLING_1) ? ADC_CFGR2_ROVSE : 0U;
while (!(ADC1->ISR & ADC_ISR_EOS));
ADC1->ISR |= ADC_ISR_EOS;
// start conversion
signal->adc->CR |= ADC_CR_ADSTART;
while (!(signal->adc->ISR & ADC_ISR_EOC));
uint16_t res = signal->adc->DR;
while (!(signal->adc->ISR & ADC_ISR_EOS));
signal->adc->ISR |= ADC_ISR_EOS;
return res;
}
uint16_t adc_get_mV(uint8_t channel) {
return (adc_get_raw(channel) * current_board->avdd_mV) / 65535U;
static void adc_calibrate_vdda(void) {
// ADC2 used for calibration
adc_init(ADC2);
// enable VREFINT channel
ADC3_COMMON->CCR |= ADC_CCR_VREFEN;
SYSCFG->ADC2ALT |= SYSCFG_ADC2ALT_ADC2_ROUT1;
// measure VREFINT and derive AVDD
uint16_t raw_vrefint = adc_get_raw(&(adc_signal_t){.adc = ADC2, .channel = 17U, .sample_time = SAMPLETIME_810_CYCLES, .oversampling = OVERSAMPLING_256});
adc_avdd_mV = (uint32_t) *VREFINT_CAL_ADDR * 16U * 3300U / raw_vrefint;
print(" AVDD: 0x"); puth(adc_avdd_mV); print(" mV\n");
}
uint16_t adc_get_mV(const adc_signal_t *signal) {
uint16_t ret = 0;
if (adc_avdd_mV == 0U) {
adc_calibrate_vdda();
}
if ((signal->adc == ADC1) || (signal->adc == ADC2)) {
ret = (adc_get_raw(signal) * adc_avdd_mV) / 65535U;
} else if (signal->adc == ADC3) {
ret = (adc_get_raw(signal) * adc_avdd_mV) / 4095U;
} else {}
return ret;
}

View File

@@ -0,0 +1,37 @@
#pragma once
typedef enum {
SAMPLETIME_1_CYCLE = 0,
SAMPLETIME_2_CYCLES = 1,
SAMPLETIME_8_CYCLES = 2,
SAMPLETIME_16_CYCLES = 3,
SAMPLETIME_32_CYCLES = 4,
SAMPLETIME_64_CYCLES = 5,
SAMPLETIME_387_CYCLES = 6,
SAMPLETIME_810_CYCLES = 7
} adc_sample_time_t;
typedef enum {
OVERSAMPLING_1 = 0,
OVERSAMPLING_2 = 1,
OVERSAMPLING_4 = 2,
OVERSAMPLING_8 = 3,
OVERSAMPLING_16 = 4,
OVERSAMPLING_32 = 5,
OVERSAMPLING_64 = 6,
OVERSAMPLING_128 = 7,
OVERSAMPLING_256 = 8,
OVERSAMPLING_512 = 9,
OVERSAMPLING_1024 = 10
} adc_oversampling_t;
typedef struct {
ADC_TypeDef *adc;
uint8_t channel;
adc_sample_time_t sample_time;
adc_oversampling_t oversampling;
} adc_signal_t;
#define ADC_CHANNEL_DEFAULT(a, c) {.adc = (a), .channel = (c), .sample_time = SAMPLETIME_32_CYCLES, .oversampling = OVERSAMPLING_64}
#define VREFINT_CAL_ADDR ((uint16_t *)0x1FF1E860UL)

View File

@@ -1,4 +1,3 @@
#if defined(ENABLE_SPI) || defined(BOOTSTUB)
// master -> panda DMA start
void llspi_mosi_dma(uint8_t *addr, int len) {
// disable DMA + SPI
@@ -106,4 +105,3 @@ void llspi_init(void) {
NVIC_EnableIRQ(DMA2_Stream3_IRQn);
NVIC_EnableIRQ(SPI4_IRQn);
}
#endif

View File

@@ -44,11 +44,6 @@ void uart_tx_ring(uart_ring *q){
EXIT_CRITICAL();
}
void uart_set_baud(USART_TypeDef *u, unsigned int baud) {
// UART7 is connected to APB1 at 60MHz
u->BRR = 60000000U / baud;
}
// This read after reading ISR clears all error interrupts. We don't want compiler warnings, nor optimizations
#define UART_READ_RDR(uart) volatile uint8_t t = (uart)->RDR; UNUSED(t);
@@ -90,11 +85,12 @@ static void uart_interrupt_handler(uart_ring *q) {
static void UART7_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_som_debug); }
void uart_init(uart_ring *q, int baud) {
void uart_init(uart_ring *q, unsigned int baud) {
if (q->uart == UART7) {
REGISTER_INTERRUPT(UART7_IRQn, UART7_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_7)
uart_set_baud(q->uart, baud);
// UART7 is connected to APB1 at 60MHz
q->uart->BRR = 60000000U / baud;
q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
// Enable interrupt on RX not empty

View File

@@ -9,7 +9,6 @@ static void gpio_usb_init(void) {
GPIOA->OSPEEDR = GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12;
}
#ifdef ENABLE_SPI
void gpio_spi_init(void) {
set_gpio_alternate(GPIOE, 11, GPIO_AF5_SPI4);
set_gpio_alternate(GPIOE, 12, GPIO_AF5_SPI4);
@@ -17,7 +16,6 @@ void gpio_spi_init(void) {
set_gpio_alternate(GPIOE, 14, GPIO_AF5_SPI4);
register_set_bits(&(GPIOE->OSPEEDR), GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12 | GPIO_OSPEEDR_OSPEED13 | GPIO_OSPEEDR_OSPEED14);
}
#endif
#ifdef BOOTSTUB
void gpio_usart2_init(void) {
@@ -110,6 +108,7 @@ void peripherals_init(void) {
// Analog
RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN; // Enable ADC12 clocks
RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; // Enable ADC3 clocks
RCC->APB1LENR |= RCC_APB1LENR_DAC12EN; // DAC
// Audio
@@ -128,7 +127,6 @@ void peripherals_init(void) {
#ifdef PANDA_JUNGLE
RCC->AHB3ENR |= RCC_AHB3ENR_SDMMC1EN; // SDMMC
RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; // Enable ADC3 clocks
#endif
}

View File

@@ -104,11 +104,33 @@ void sound_init(void) {
REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA)
REGISTER_INTERRUPT(DMA1_Stream0_IRQn, DMA1_Stream0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA)
// *** tmp, remove soon ***
static const uint8_t olds[][12] = {
{0x44, 0x00, 0x10, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x14, 0x00, 0x13, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x04, 0x00, 0x30, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x2f, 0x00, 0x14, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x1e, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x26, 0x00, 0x15, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x35, 0x00, 0x32, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x37, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
};
bool is_old = false;
for (uint8_t i = 0U; i < (sizeof(olds) / sizeof(olds[0])); i++) {
is_old |= (memcmp(olds[i], ((uint8_t *)UID_BASE), 12) == 0);
}
// *** tmp end ***
// Init DAC
DAC1->DHR12R1 = (1UL << 11);
if (!is_old) DAC1->DHR12R2 = (1UL << 11);
register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU);
register_set(&DAC1->CR, DAC_CR_TEN1 | (4U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU);
register_set_bits(&DAC1->CR, DAC_CR_EN1);
if (is_old) {
register_set_bits(&DAC1->CR, DAC_CR_EN1);
} else {
register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2);
}
// Setup DMAMUX (DAC_CH1_DMA as input)
register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk);

View File

@@ -1,5 +1,5 @@
#include "stm32h7/inc/stm32h7xx.h"
#include "stm32h7/inc/stm32h7xx_hal_gpio_ex.h"
#include "stm32h7xx.h"
#include "stm32h7xx_hal_gpio_ex.h"
#define MCU_IDCODE 0x483U
#define CORE_FREQ 240U // in Mhz
@@ -43,45 +43,49 @@ separate IRQs for RX and TX.
#define PROVISION_CHUNK_ADDRESS 0x080FFFE0U
#define DEVICE_SERIAL_NUMBER_ADDRESS 0x080FFFC0U
#include "can.h"
#include "comms_definitions.h"
#include "board/can.h"
#include "board/comms_definitions.h"
#ifndef BOOTSTUB
#include "main_definitions.h"
#include "board/main_definitions.h"
#else
#include "bootstub_declarations.h"
#include "board/bootstub_declarations.h"
#endif
#include "libc.h"
#include "critical.h"
#include "faults.h"
#include "utils.h"
#include "board/libc.h"
#include "board/critical.h"
#include "board/faults.h"
#include "board/utils.h"
#include "drivers/registers.h"
#include "drivers/interrupts.h"
#include "drivers/gpio.h"
#include "stm32h7/peripherals.h"
#include "stm32h7/interrupt_handlers.h"
#include "drivers/timers.h"
#include "board/drivers/registers.h"
#include "board/drivers/interrupts.h"
#include "board/drivers/gpio.h"
#include "board/stm32h7/peripherals.h"
#include "board/stm32h7/interrupt_handlers.h"
#include "board/drivers/timers.h"
#if !defined(BOOTSTUB)
#include "drivers/uart.h"
#include "stm32h7/lluart.h"
#include "board/drivers/uart.h"
#include "board/stm32h7/lluart.h"
#endif
#include "stm32h7/board.h"
#include "stm32h7/clock.h"
#ifdef PANDA_JUNGLE
#include "board/jungle/stm32h7/board.h"
#else
#include "board/stm32h7/board.h"
#endif
#include "board/stm32h7/clock.h"
#ifdef BOOTSTUB
#include "stm32h7/llflash.h"
#include "board/stm32h7/llflash.h"
#else
#include "stm32h7/llfdcan.h"
#include "board/stm32h7/llfdcan.h"
#endif
#include "stm32h7/llusb.h"
#include "board/stm32h7/llusb.h"
#include "drivers/spi.h"
#include "stm32h7/llspi.h"
#include "board/drivers/spi.h"
#include "board/stm32h7/llspi.h"
void early_gpio_float(void) {
RCC->AHB4ENR = RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_GPIOFEN | RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_GPIOHEN;

View File

@@ -75,7 +75,7 @@ if __name__ == "__main__":
if args.bus:
bus = int(args.bus)
else:
bus = 1 if panda.has_obd() else 0
bus = 1
rx_addr = addr + int(args.rxoffset, base=16) if args.rxoffset else None
# Try all sub-addresses for addr. By default, this is None

View File

@@ -3,7 +3,7 @@ name = "pandacan"
version = "0.0.10"
description = "Code powering the comma.ai panda"
readme = "README.md"
requires-python = ">=3.11,<3.13"
requires-python = ">=3.11,<3.13" # macOS doesn't work with 3.13 due to pycapnp from opendbc
license = {text = "MIT"}
authors = [{name = "comma.ai"}]
classifiers = [
@@ -13,7 +13,7 @@ classifiers = [
]
dependencies = [
"libusb1",
"opendbc @ git+https://github.com/sunnypilot/opendbc.git@c87940a6b5f0b74b8df95f5ef24d4003e7b932d4#egg=opendbc",
"opendbc @ git+https://github.com/sunnypilot/opendbc.git@master#egg=opendbc",
]
[project.optional-dependencies]
@@ -24,7 +24,6 @@ dev = [
"flaky",
"pytest",
"pytest-mock",
"pytest-xdist",
"pytest-timeout",
"pytest-randomly",
"ruff",
@@ -69,7 +68,7 @@ flake8-implicit-str-concat.allow-multiline=false
"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!"
[tool.pytest.ini_options]
addopts = "-n0 -Werror --strict-config --strict-markers --durations=10 --ignore-glob='*.sh' --ignore=tests/misra --ignore=tests/som --ignore=tests/hitl"
addopts = "-Werror --strict-config --strict-markers --durations=10 --ignore-glob='*.sh' --ignore=tests/misra --ignore=tests/som --ignore=tests/hitl"
python_files = "test_*.py"
testpaths = [
"tests/"

View File

@@ -105,9 +105,6 @@ ensure_health_packet_version = partial(ensure_version, "health", "HEALTH_PACKET_
class Panda:
SERIAL_DEBUG = 0
SERIAL_ESP = 1
SERIAL_LIN1 = 2
SERIAL_LIN2 = 3
SERIAL_SOM_DEBUG = 4
USB_VIDS = (0xbbaa, 0x3801) # 0x3801 is comma's registered VID
@@ -115,15 +112,10 @@ class Panda:
REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
# from https://github.com/commaai/openpilot/blob/103b4df18cbc38f4129555ab8b15824d1a672bdf/cereal/log.capnp#L648
HW_TYPE_UNKNOWN = b'\x00'
HW_TYPE_WHITE_PANDA = b'\x01'
HW_TYPE_GREY_PANDA = b'\x02'
HW_TYPE_BLACK_PANDA = b'\x03'
HW_TYPE_PEDAL = b'\x04'
HW_TYPE_UNO = b'\x05'
HW_TYPE_DOS = b'\x06'
HW_TYPE_RED_PANDA = b'\x07'
HW_TYPE_RED_PANDA_V2 = b'\x08'
HW_TYPE_TRES = b'\x09'
HW_TYPE_CUATRO = b'\x0a'
@@ -133,14 +125,12 @@ class Panda:
HEALTH_STRUCT = struct.Struct("<IIIIIIIIBBBBBHBBBHfBBHBHHB")
CAN_HEALTH_STRUCT = struct.Struct("<BIBBBBBBBBIIIIIIIHHBBBIIII")
F4_DEVICES = [HW_TYPE_WHITE_PANDA, HW_TYPE_GREY_PANDA, HW_TYPE_BLACK_PANDA, HW_TYPE_UNO, HW_TYPE_DOS]
H7_DEVICES = [HW_TYPE_RED_PANDA, HW_TYPE_RED_PANDA_V2, HW_TYPE_TRES, HW_TYPE_CUATRO]
F4_DEVICES = [HW_TYPE_DOS, ]
H7_DEVICES = [HW_TYPE_RED_PANDA, HW_TYPE_TRES, HW_TYPE_CUATRO]
INTERNAL_DEVICES = (HW_TYPE_UNO, HW_TYPE_DOS, HW_TYPE_TRES, HW_TYPE_CUATRO)
HAS_OBD = (HW_TYPE_BLACK_PANDA, HW_TYPE_UNO, HW_TYPE_DOS, HW_TYPE_RED_PANDA, HW_TYPE_RED_PANDA_V2, HW_TYPE_TRES, HW_TYPE_CUATRO)
INTERNAL_DEVICES = (HW_TYPE_DOS, HW_TYPE_TRES, HW_TYPE_CUATRO)
MAX_FAN_RPMs = {
HW_TYPE_UNO: 5100,
HW_TYPE_DOS: 6500,
HW_TYPE_TRES: 6600,
HW_TYPE_CUATRO: 12500,
@@ -522,16 +512,16 @@ class Panda:
dfu_list = PandaDFU.list()
return True
@staticmethod
def wait_for_panda(serial: str | None, timeout: int) -> bool:
@classmethod
def wait_for_panda(cls, serial: str | None, timeout: int) -> bool:
t_start = time.monotonic()
serials = Panda.list()
serials = cls.list()
while (serial is None and len(serials) == 0) or (serial is not None and serial not in serials):
logger.debug("waiting for panda...")
time.sleep(0.1)
if timeout is not None and (time.monotonic() - t_start) > timeout:
return False
serials = Panda.list()
serials = cls.list()
return True
def up_to_date(self, fn=None) -> bool:
@@ -572,7 +562,7 @@ class Panda:
"interrupt_load": a[18],
"fan_power": a[19],
"safety_rx_checks_invalid": a[20],
"spi_checksum_error_count": a[21],
"spi_error_count": a[21],
"fan_stall_count": a[22],
"sbu1_voltage_mV": a[23],
"sbu2_voltage_mV": a[24],
@@ -669,9 +659,6 @@ class Panda:
raise ValueError(f"unknown HW type: {hw_type}")
def has_obd(self):
return self.get_type() in Panda.HAS_OBD
def is_internal(self):
return self.get_type() in Panda.INTERNAL_DEVICES
@@ -816,16 +803,6 @@ class Panda:
ret += self._handle.bulkWrite(2, struct.pack("B", port_number) + ln[i:i + 0x20])
return ret
def serial_clear(self, port_number):
"""Clears all messages (tx and rx) from the specified internal uart
ringbuffer as though it were drained.
Args:
port_number (int): port number of the uart to clear.
"""
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf2, port_number, 0, b'')
def send_heartbeat(self, engaged=True, engaged_mads=True):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf3, engaged, engaged_mads, b'')

View File

@@ -1,157 +0,0 @@
#!/usr/bin/env python3
# Loopback test between black panda (+ harness and power) and white/grey panda
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations
import os
import time
import random
import argparse
from opendbc.car.structs import CarParams
from panda import Panda
def get_test_string():
return b"test" + os.urandom(10)
counter = 0
nonzero_bus_errors = 0
zero_bus_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
raise Exception("Connect white/grey and black panda to run this test!")
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
black_panda = None
other_panda = None
# find out which one is black
if pandas[0].is_black() and not pandas[1].is_black():
black_panda = pandas[0]
other_panda = pandas[1]
elif not pandas[0].is_black() and pandas[1].is_black():
black_panda = pandas[1]
other_panda = pandas[0]
else:
raise Exception("Connect white/grey and black panda to run this test!")
# disable safety modes
black_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
other_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
while True:
test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration)
test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration)
counter += 1
print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors)
# Toggle relay
black_panda.set_safety_mode(CarParams.SafetyModel.silent)
time.sleep(1)
black_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
time.sleep(1)
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration):
global nonzero_bus_errors, zero_bus_errors, content_errors
if direction:
print("***************** TESTING (BLACK --> OTHER) *****************")
else:
print("***************** TESTING (OTHER --> BLACK) *****************")
for send_bus, obd, recv_buses in test_array:
black_panda.send_heartbeat()
other_panda.send_heartbeat()
print("\ntest can: ", send_bus, " OBD: ", obd)
# set OBD on black panda
black_panda.set_obd(True if obd else None)
# clear and flush
if direction:
black_panda.can_clear(send_bus)
else:
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
if direction:
other_panda.can_clear(recv_bus)
else:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
if direction:
black_panda.can_send(at, st, send_bus)
else:
other_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
if direction:
_ = black_panda.can_recv() # can echo
cans_loop = other_panda.can_recv()
else:
_ = other_panda.can_recv() # can echo
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[1] != st):
content_errors += 1
print(" Loop on bus", str(loop[2]))
loop_buses.append(loop[2])
if len(cans_loop) == 0:
print(" No loop")
assert not os.getenv("NOASSERT")
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
if len(loop_buses) == 0:
zero_bus_errors += 1
else:
nonzero_bus_errors += 1
assert not os.getenv("NOASSERT")
else:
print(" TEST PASSED")
time.sleep(sleep_duration)
print("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for _ in range(args.n):
run_test(sleep_duration=args.sleep)

View File

@@ -1,165 +0,0 @@
#!/usr/bin/env python3
# Loopback test between black panda (+ harness and power) and white/grey panda
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations
import os
import time
import random
import argparse
from opendbc.car.structs import CarParams
from panda import Panda
def get_test_string():
return b"test" + os.urandom(10)
counter = 0
nonzero_bus_errors = 0
zero_bus_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
raise Exception("Connect white/grey and black panda to run this test!")
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
black_panda = None
other_panda = None
# find out which one is black
if pandas[0].is_black() and not pandas[1].is_black():
black_panda = pandas[0]
other_panda = pandas[1]
elif not pandas[0].is_black() and pandas[1].is_black():
black_panda = pandas[1]
other_panda = pandas[0]
else:
raise Exception("Connect white/grey and black panda to run this test!")
# disable safety modes
black_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
other_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
start_time = time.time()
temp_start_time = start_time
while True:
test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration)
test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration)
counter += 1
runtime = time.time() - start_time
print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors,
"Content errors:", content_errors, "Runtime: ", runtime)
if (time.time() - temp_start_time) > 3600 * 6:
# Toggle relay
black_panda.set_safety_mode(CarParams.SafetyModel.silent)
time.sleep(1)
black_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
time.sleep(1)
temp_start_time = time.time()
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration):
global nonzero_bus_errors, zero_bus_errors, content_errors
if direction:
print("***************** TESTING (BLACK --> OTHER) *****************")
else:
print("***************** TESTING (OTHER --> BLACK) *****************")
for send_bus, obd, recv_buses in test_array:
black_panda.send_heartbeat()
other_panda.send_heartbeat()
print("\ntest can: ", send_bus, " OBD: ", obd)
# set OBD on black panda
black_panda.set_obd(True if obd else None)
# clear and flush
if direction:
black_panda.can_clear(send_bus)
else:
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
if direction:
other_panda.can_clear(recv_bus)
else:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
if direction:
black_panda.can_send(at, st, send_bus)
else:
other_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
if direction:
_ = black_panda.can_recv() # cans echo
cans_loop = other_panda.can_recv()
else:
_ = other_panda.can_recv() # cans echo
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[1] != st):
content_errors += 1
print(" Loop on bus", str(loop[2]))
loop_buses.append(loop[2])
if len(cans_loop) == 0:
print(" No loop")
assert os.getenv("NOASSERT")
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
if len(loop_buses) == 0:
zero_bus_errors += 1
else:
nonzero_bus_errors += 1
assert os.getenv("NOASSERT")
else:
print(" TEST PASSED")
time.sleep(sleep_duration)
print("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for _ in range(args.n):
run_test(sleep_duration=args.sleep)

View File

@@ -1,136 +0,0 @@
#!/usr/bin/env python3
# Relay test with loopback between black panda (+ harness and power) and white/grey panda
# Tests the relay switching multiple times / second by looking at the buses on which loop occurs.
import os
import time
import random
import argparse
from opendbc.car.structs import CarParams
from panda import Panda
def get_test_string():
return b"test" + os.urandom(10)
counter = 0
open_errors = 0
closed_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter, open_errors, closed_errors
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
raise Exception("Connect white/grey and black panda to run this test!")
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
# find out which one is black
type0 = pandas[0].get_type()
type1 = pandas[1].get_type()
black_panda = None
other_panda = None
if type0 == "\x03" and type1 != "\x03":
black_panda = pandas[0]
other_panda = pandas[1]
elif type0 != "\x03" and type1 == "\x03":
black_panda = pandas[1]
other_panda = pandas[0]
else:
raise Exception("Connect white/grey and black panda to run this test!")
# disable safety modes
black_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
other_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
while True:
# Switch on relay
black_panda.set_safety_mode(CarParams.SafetyModel.allOutput)
time.sleep(0.05)
if not test_buses(black_panda, other_panda, (0, False, [0])):
open_errors += 1
raise Exception("Open error")
# Switch off relay
black_panda.set_safety_mode(CarParams.SafetyModel.silent)
time.sleep(0.05)
if not test_buses(black_panda, other_panda, (0, False, [0, 2])):
closed_errors += 1
raise Exception("Close error")
counter += 1
print("Number of cycles:", counter, "Open errors:", open_errors, "Closed errors:", closed_errors, "Content errors:", content_errors)
def test_buses(black_panda, other_panda, test_obj):
global content_errors
send_bus, obd, recv_buses = test_obj
black_panda.send_heartbeat()
other_panda.send_heartbeat()
# Set OBD on send panda
other_panda.set_obd(True if obd else None)
# clear and flush
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
other_panda.can_send(at, st, send_bus)
time.sleep(0.05)
# check for receive
_ = other_panda.can_recv() # can echo
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[1] != st):
content_errors += 1
loop_buses.append(loop[2])
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
return False
else:
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for _ in range(args.n):
run_test(sleep_duration=args.sleep)

View File

@@ -12,11 +12,17 @@ if [[ $PLATFORM == "Darwin" ]]; then
brew install --cask gcc-arm-embedded
brew install python3 gcc@13
elif [[ $PLATFORM == "Linux" ]]; then
# for AGNOS since we clear the apt lists
if [[ ! -d /"var/lib/apt/" ]]; then
sudo apt update
fi
sudo apt-get install -y --no-install-recommends \
curl \
curl ca-certificates \
make g++ git libnewlib-arm-none-eabi \
libusb-1.0-0 \
gcc-arm-none-eabi python3-pip python3-venv python3-dev
gcc-arm-none-eabi \
python3-dev python3-pip python3-venv
else
echo "WARNING: unsupported platform. skipping apt/brew install."
fi

View File

@@ -17,5 +17,5 @@ mypy python/
# *** test ***
# TODO: make xdist and randomly work
pytest -n0 --randomly-dont-reorganize tests/
# TODO: make randomly work
pytest --randomly-dont-reorganize tests/

View File

@@ -1,10 +1,15 @@
import time
import pytest
from flaky import flaky
from opendbc.car.structs import CarParams
from panda import Panda
from panda.tests.hitl.helpers import time_many_sends
pytestmark = [
pytest.mark.test_panda_types((Panda.HW_TYPE_DOS, Panda.HW_TYPE_RED_PANDA))
]
def test_can_loopback(p):
p.set_safety_mode(CarParams.SafetyModel.allOutput)
p.set_can_loopback(True)

View File

@@ -7,7 +7,6 @@ from flaky import flaky
from collections import defaultdict
from opendbc.car.structs import CarParams
from panda.tests.hitl.conftest import PandaGroup
from panda.tests.hitl.helpers import time_many_sends, get_random_can_messages, clear_can_buffers
@flaky(max_runs=3, min_passes=1)
@@ -90,7 +89,6 @@ def test_latency(p, panda_jungle):
@pytest.mark.panda_expect_can_error
@pytest.mark.test_panda_types(PandaGroup.GEN2)
def test_gen2_loopback(p, panda_jungle):
def test(p_send, p_recv, address=None):
for bus in range(4):

View File

@@ -82,12 +82,12 @@ class TestSpi:
self._ping(mocker, p)
def test_bad_checksum(self, mocker, p):
cnt = p.health()['spi_checksum_error_count']
cnt = p.health()['spi_error_count']
with patch('panda.python.spi.PandaSpiHandle._calc_checksum', return_value=0):
with pytest.raises(PandaSpiNackResponse):
p._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, p.HEALTH_STRUCT.size, timeout=50)
self._ping(mocker, p)
assert (p.health()['spi_checksum_error_count'] - cnt) > 0
assert (p.health()['spi_error_count'] - cnt) > 0
def test_non_existent_endpoint(self, mocker, p):
for _ in range(10):

View File

@@ -4,12 +4,10 @@ import itertools
from opendbc.car.structs import CarParams
from panda import Panda
from panda.tests.hitl.conftest import PandaGroup
# TODO: test relay
@pytest.mark.panda_expect_can_error
@pytest.mark.test_panda_types(PandaGroup.GEN2)
def test_harness_status(p, panda_jungle):
# map from jungle orientations to panda orientations
orientation_map = {
@@ -61,7 +59,7 @@ def test_harness_status(p, panda_jungle):
assert buses[2] == (0 if flipped else 2)
# SBU voltages
supply_voltage_mV = 1800 if p.get_type() in [Panda.HW_TYPE_TRES, ] else 3300
supply_voltage_mV = 1800 if p.get_type() in [Panda.HW_TYPE_TRES, Panda.HW_TYPE_CUATRO] else 3300
if orientation == Panda.HARNESS_STATUS_NC:
assert health['sbu1_voltage_mV'] > 0.9 * supply_voltage_mV

View File

@@ -1,62 +1,32 @@
import os
import pytest
import concurrent.futures
from panda import Panda, PandaDFU, PandaJungle
from panda.tests.hitl.helpers import clear_can_buffers
# needed to get output when using xdist
if "DEBUG" in os.environ:
import sys
sys.stdout = sys.stderr
SPEED_NORMAL = 500
BUS_SPEEDS = [(0, SPEED_NORMAL), (1, SPEED_NORMAL), (2, SPEED_NORMAL)]
JUNGLE_SERIAL = os.getenv("PANDAS_JUNGLE")
# test options
NO_JUNGLE = os.environ.get("NO_JUNGLE", "0") == "1"
PANDAS_EXCLUDE = os.getenv("PANDAS_EXCLUDE", "").strip().split(" ")
HW_TYPES = os.environ.get("HW_TYPES", None)
PARALLEL = "PARALLEL" in os.environ
NON_PARALLEL = "NON_PARALLEL" in os.environ
if PARALLEL:
NO_JUNGLE = True
class PandaGroup:
H7 = (Panda.HW_TYPE_RED_PANDA, Panda.HW_TYPE_RED_PANDA_V2, Panda.HW_TYPE_TRES)
GEN2 = (Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_UNO, Panda.HW_TYPE_DOS) + H7
TESTED = (Panda.HW_TYPE_WHITE_PANDA, Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_RED_PANDA, Panda.HW_TYPE_RED_PANDA_V2, Panda.HW_TYPE_UNO)
if HW_TYPES is not None:
PandaGroup.TESTED = [bytes([int(x), ]) for x in HW_TYPES.strip().split(",")] # type: ignore
# Find all pandas connected
_all_pandas = {}
_panda_jungle = None
def init_all_pandas():
_panda_type = None
_panda_serial = None
def init_devices():
if not NO_JUNGLE:
global _panda_jungle
_panda_jungle = PandaJungle(JUNGLE_SERIAL)
_panda_jungle = PandaJungle()
_panda_jungle.set_panda_power(True)
for serial in Panda.list():
if serial not in PANDAS_EXCLUDE:
with Panda(serial=serial, claim=False) as p:
ptype = bytes(p.get_type())
if ptype in PandaGroup.TESTED:
_all_pandas[serial] = ptype
# ensure we have all tested panda types
missing_types = set(PandaGroup.TESTED) - set(_all_pandas.values())
assert len(missing_types) == 0, f"Missing panda types: {missing_types}"
print(f"{len(_all_pandas)} total pandas")
init_all_pandas()
_all_panda_serials = sorted(_all_pandas.keys())
with Panda(serial=None, claim=False) as p:
global _panda_type
global _panda_serial
_panda_serial = p.get_usb_serial()
_panda_type = bytes(p.get_type())
assert _panda_serial is not None, "No panda found!"
init_devices()
def init_jungle():
if _panda_jungle is None:
@@ -90,24 +60,9 @@ def pytest_collection_modifyitems(items):
if item.get_closest_marker('timeout') is None:
item.add_marker(pytest.mark.timeout(60))
# xdist grouping by panda
serial = item.name.split("serial=")[1].split(",")[0]
assert len(serial) == 24
item.add_marker(pytest.mark.xdist_group(serial))
needs_jungle = "panda_jungle" in item.fixturenames
if PARALLEL and needs_jungle:
item.add_marker(pytest.mark.skip(reason="no jungle tests in PARALLEL mode"))
elif NON_PARALLEL and not needs_jungle:
item.add_marker(pytest.mark.skip(reason="only running jungle tests"))
def pytest_make_parametrize_id(config, val, argname):
if val in _all_pandas:
# TODO: get nice string instead of int
hw_type = _all_pandas[val][0]
return f"serial={val}, hw_type={hw_type}"
return None
if needs_jungle and NO_JUNGLE:
item.add_marker(pytest.mark.skip(reason="skipping tests that requires a jungle"))
@pytest.fixture(name='panda_jungle', scope='function')
def fixture_panda_jungle(request):
@@ -116,6 +71,8 @@ def fixture_panda_jungle(request):
@pytest.fixture(name='p', scope='function')
def func_fixture_panda(request, module_panda):
# *** Setup ***
p = module_panda
# Check if test is applicable to this panda
@@ -123,14 +80,14 @@ def func_fixture_panda(request, module_panda):
if mark:
assert len(mark.args) > 0, "Missing panda types argument in mark"
test_types = mark.args[0]
if _all_pandas[p.get_usb_serial()] not in test_types:
if _panda_type not in test_types:
pytest.skip(f"Not applicable, {test_types} pandas only")
mark = request.node.get_closest_marker('skip_panda_types')
if mark:
assert len(mark.args) > 0, "Missing panda types argument in mark"
skip_types = mark.args[0]
if _all_pandas[p.get_usb_serial()] in skip_types:
if _panda_type in skip_types:
pytest.skip(f"Not applicable to {skip_types}")
# this is 2+ seconds on USB pandas due to slow
@@ -140,10 +97,10 @@ def func_fixture_panda(request, module_panda):
# ensure FW hasn't changed
assert p.up_to_date()
# Run test
# *** Run test ***
yield p
# Teardown
# *** Teardown ***
# reconnect
if p.get_dfu_serial() in PandaDFU.list():
@@ -164,7 +121,7 @@ def func_fixture_panda(request, module_panda):
assert p.health()['fault_status'] == 0
# Check for SPI errors
#assert p.health()['spi_checksum_error_count'] == 0
#assert p.health()['spi_error_count'] == 0
# Check health of each CAN core after test, normal to fail for test_gen2_loopback on OBD bus, so skipping
mark = request.node.get_closest_marker('panda_expect_can_error')
@@ -182,41 +139,27 @@ def func_fixture_panda(request, module_panda):
assert can_health['total_error_cnt'] == 0
assert can_health['total_tx_checksum_error_cnt'] == 0
@pytest.fixture(name='module_panda', params=_all_panda_serials, scope='module')
@pytest.fixture(name='module_panda', scope='module')
def fixture_panda_setup(request):
"""
Clean up all pandas + jungle and return the panda under test.
Clean up panda + jungle and return the panda under test.
"""
panda_serial = request.param
# Initialize jungle
# init jungle
init_jungle()
# Connect to pandas
def cnnct(s):
if s == panda_serial:
p = Panda(serial=s)
p.reset(reconnect=True)
# init panda
p = Panda(serial=_panda_serial)
p.reset(reconnect=True)
p.set_can_loopback(False)
p.set_power_save(False)
for bus, speed in BUS_SPEEDS:
p.set_can_speed_kbps(bus, speed)
clear_can_buffers(p)
p.set_power_save(False)
return p
elif not PARALLEL:
with Panda(serial=s) as p:
p.reset(reconnect=False)
return None
with concurrent.futures.ThreadPoolExecutor() as exc:
ps = list(exc.map(cnnct, _all_panda_serials, timeout=20))
pandas = [p for p in ps if p is not None]
p.set_can_loopback(False)
p.set_power_save(False)
for bus, speed in BUS_SPEEDS:
p.set_can_speed_kbps(bus, speed)
clear_can_buffers(p)
p.set_power_save(False)
# run test
yield pandas[0]
yield p
# Teardown
for p in pandas:
p.close()
# teardown
p.close()

View File

@@ -4,7 +4,9 @@ import concurrent.futures
from panda import PandaJungle, PandaJungleDFU, McuType
from panda.tests.libs.resetter import Resetter
SERIALS = {'180019001451313236343430', '1d0017000c50435635333720'}
SERIALS = {
'180019001451313236343430', # jungle v2
}
def recover(s):
with PandaJungleDFU(s) as pd:
@@ -25,9 +27,9 @@ if __name__ == "__main__":
for i in range(1, 4):
r.enable_power(i, 0)
r.cycle_power(ports=[1, 2], dfu=True)
for s in SERIALS:
assert PandaJungle.wait_for_dfu(PandaJungleDFU.st_serial_to_dfu_serial(s, McuType.H7), timeout=10)
dfu_serials = PandaJungleDFU.list()
print(len(dfu_serials), len(SERIALS))
assert len(dfu_serials) == len(SERIALS)
with concurrent.futures.ProcessPoolExecutor(max_workers=len(dfu_serials)) as exc:
@@ -36,7 +38,9 @@ if __name__ == "__main__":
# power cycle for H7 bootloader bug
r.cycle_power(ports=[1, 2])
serials = PandaJungle.list()
# wait for them to come back up
for s in SERIALS:
assert PandaJungle.wait_for_panda(s, timeout=10)
assert set(PandaJungle.list()) >= SERIALS
mcu_types = list(exc.map(flash, SERIALS, timeout=20))
assert set(mcu_types) == {McuType.F4, McuType.H7}
assert set(mcu_types) == {McuType.H7, }

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -e
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd $DIR
# n = number of pandas tested
PARALLEL=1 pytest --durations=0 *.py -n 5 --dist loadgroup -x

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd $DIR
NON_PARALLEL=1 pytest --durations=0 *.py -x

View File

@@ -42,16 +42,17 @@ class Resetter():
def enable_boot(self, enabled):
self._handle.controlWrite((usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE), 0xff, 0, enabled, b'')
def cycle_power(self, delay=5, dfu=False, ports=None):
def cycle_power(self, dfu=False, ports=None):
if ports is None:
ports = [1, 2, 3]
self.enable_boot(dfu)
for port in ports:
self.enable_power(port, False)
time.sleep(0.5)
time.sleep(0.05)
for port in ports:
self.enable_power(port, True)
time.sleep(delay)
time.sleep(0.05)
self.enable_boot(False)
time.sleep(0.12) # takes the kernel this long to detect the disconnect

View File

@@ -5,7 +5,7 @@ Cppcheck checkers list from test_misra.sh:
TEST variant options:
--enable=all --disable=unusedFunction -DPANDA --addon=misra -DSTM32F4 -DSTM32F413xx /board/main.c
--enable=all --disable=unusedFunction --addon=misra -DSTM32F4 -DSTM32F413xx -I /board/stm32f4/inc/ /board/main.c
Critical errors
@@ -460,7 +460,7 @@ Not available, Cppcheck Premium is not used
TEST variant options:
--enable=all --disable=unusedFunction -DPANDA --addon=misra -DSTM32H7 -DSTM32H725xx /board/main.c
--enable=all --disable=unusedFunction --addon=misra -DSTM32H7 -DSTM32H725xx -I /board/stm32h7/inc/ /board/main.c
Critical errors

View File

@@ -37,7 +37,7 @@ echo "Cppcheck checkers list from test_misra.sh:" > $CHECKLIST
cppcheck() {
# get all gcc defines: arm-none-eabi-gcc -dM -E - < /dev/null
COMMON_DEFINES="-D__GNUC__=9 -UCMSIS_NVIC_VIRTUAL -UCMSIS_VECTAB_VIRTUAL"
COMMON_DEFINES="-D__GNUC__=9 -UCMSIS_NVIC_VIRTUAL -UCMSIS_VECTAB_VIRTUAL -UPANDA_JUNGLE -UBOOTSTUB"
# note that cppcheck build cache results in inconsistent results as of v2.13.0
OUTPUT=$DIR/.output.log
@@ -45,9 +45,9 @@ cppcheck() {
echo -e "\n\n\n\n\nTEST variant options:" >> $CHECKLIST
echo -e ""${@//$PANDA_DIR/}"\n\n" >> $CHECKLIST # (absolute path removed)
$CPPCHECK_DIR/cppcheck --inline-suppr -I $PANDA_DIR/board/ \
$CPPCHECK_DIR/cppcheck --inline-suppr \
-I $PANDA_DIR \
-I "$(arm-none-eabi-gcc -print-file-name=include)" \
-I $PANDA_DIR/board/stm32f4/inc/ -I $PANDA_DIR/board/stm32h7/inc/ \
-I $OPENDBC_ROOT \
--suppressions-list=$DIR/suppressions.txt --suppress=*:*inc/* \
--suppress=*:*include/* --error-exitcode=2 --check-level=exhaustive --safety \
@@ -64,13 +64,13 @@ cppcheck() {
fi
}
PANDA_OPTS="--enable=all --disable=unusedFunction -DPANDA --addon=misra"
PANDA_OPTS="--enable=all --disable=unusedFunction --addon=misra"
printf "\n${GREEN}** PANDA F4 CODE **${NC}\n"
cppcheck $PANDA_OPTS -DSTM32F4 -DSTM32F413xx $PANDA_DIR/board/main.c
cppcheck $PANDA_OPTS -DSTM32F4 -DSTM32F413xx -I $PANDA_DIR/board/stm32f4/inc/ $PANDA_DIR/board/main.c
printf "\n${GREEN}** PANDA H7 CODE **${NC}\n"
cppcheck $PANDA_OPTS -DSTM32H7 -DSTM32H725xx $PANDA_DIR/board/main.c
cppcheck $PANDA_OPTS -DSTM32H7 -DSTM32H725xx -I $PANDA_DIR/board/stm32h7/inc/ $PANDA_DIR/board/main.c
# unused needs to run globally
#printf "\n${GREEN}** UNUSED ALL CODE **${NC}\n"

View File

@@ -4,7 +4,7 @@ import pytest
from opendbc.car.structs import CarParams
from panda import Panda, PandaJungle
PANDA_SERIAL = "28002d000451323431333839"
PANDA_SERIAL = "300008001851333037333932"
JUNGLE_SERIAL = "26001c001451313236343430"
OBDC_PORT = 1