openpilot v0.9.6 release
date: 2024-01-12T10:13:37 master commit: ba792d576a49a0899b88a753fa1c52956bedf9e6
This commit is contained in:
26
panda/board/jungle/README.md
Normal file
26
panda/board/jungle/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
Welcome to the jungle
|
||||
======
|
||||
|
||||
Firmware for the Panda Jungle testing board.
|
||||
Available for purchase at the [comma shop](https://comma.ai/shop/panda-jungle).
|
||||
|
||||
## udev rules
|
||||
|
||||
To make the jungle usable without root permissions, you might need to setup udev rules for it.
|
||||
On ubuntu, this should do the trick:
|
||||
``` bash
|
||||
sudo tee /etc/udev/rules.d/12-panda_jungle.rules <<EOF
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddcf", MODE="0666"
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddef", MODE="0666"
|
||||
EOF
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
```
|
||||
|
||||
## updating the firmware
|
||||
Updating the firmware is easy! In the `board/jungle/` folder, run:
|
||||
``` bash
|
||||
./flash.py
|
||||
```
|
||||
|
||||
If you somehow bricked your jungle, you'll need a [comma key](https://comma.ai/shop/products/comma-key) to put the microcontroller in DFU mode for the V1.
|
||||
For V2, the onboard button serves this purpose. When powered on while holding the button to put it in DFU mode, running `./recover.sh` in `board/` should unbrick it.
|
||||
18
panda/board/jungle/SConscript
Normal file
18
panda/board/jungle/SConscript
Normal file
@@ -0,0 +1,18 @@
|
||||
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)
|
||||
160
panda/board/jungle/__init__.py
Normal file
160
panda/board/jungle/__init__.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# python library to interface with panda
|
||||
import os
|
||||
import struct
|
||||
from functools import wraps
|
||||
from typing import Optional
|
||||
|
||||
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/")
|
||||
|
||||
|
||||
def ensure_jungle_health_packet_version(fn):
|
||||
@wraps(fn)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.health_version != self.HEALTH_PACKET_VERSION:
|
||||
raise RuntimeError(f"Jungle firmware ({self.health_version}) doesn't match the \
|
||||
library's health packet version ({self.HEALTH_PACKET_VERSION}). \
|
||||
Reflash jungle.")
|
||||
return fn(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
class PandaJungleDFU(PandaDFU):
|
||||
def recover(self):
|
||||
fn = os.path.join(FW_PATH, self._mcu_type.config.bootstub_fn.replace("panda", "panda_jungle"))
|
||||
with open(fn, "rb") as f:
|
||||
code = f.read()
|
||||
self.program_bootstub(code)
|
||||
self.reset()
|
||||
|
||||
|
||||
class PandaJungle(Panda):
|
||||
USB_PIDS = (0xddef, 0xddcf)
|
||||
|
||||
HW_TYPE_UNKNOWN = b'\x00'
|
||||
HW_TYPE_V1 = b'\x01'
|
||||
HW_TYPE_V2 = b'\x02'
|
||||
|
||||
F4_DEVICES = [HW_TYPE_V1, ]
|
||||
H7_DEVICES = [HW_TYPE_V2, ]
|
||||
|
||||
HEALTH_PACKET_VERSION = 1
|
||||
HEALTH_STRUCT = struct.Struct("<IffffffHHHHHHHHHHHH")
|
||||
|
||||
HARNESS_ORIENTATION_NONE = 0
|
||||
HARNESS_ORIENTATION_1 = 1
|
||||
HARNESS_ORIENTATION_2 = 2
|
||||
|
||||
@classmethod
|
||||
def spi_connect(cls, serial, ignore_version=False):
|
||||
return None, None, None, None, None
|
||||
|
||||
def flash(self, fn=None, code=None, reconnect=True):
|
||||
if not fn:
|
||||
fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn.replace("panda", "panda_jungle"))
|
||||
super().flash(fn=fn, code=code, reconnect=reconnect)
|
||||
|
||||
def recover(self, timeout: Optional[int] = 60, reset: bool = True) -> bool:
|
||||
dfu_serial = self.get_dfu_serial()
|
||||
|
||||
if reset:
|
||||
self.reset(enter_bootstub=True)
|
||||
self.reset(enter_bootloader=True)
|
||||
|
||||
if not self.wait_for_dfu(dfu_serial, timeout=timeout):
|
||||
return False
|
||||
|
||||
dfu = PandaJungleDFU(dfu_serial)
|
||||
dfu.recover()
|
||||
|
||||
# reflash after recover
|
||||
self.connect(True, True)
|
||||
self.flash()
|
||||
return True
|
||||
|
||||
def get_mcu_type(self) -> McuType:
|
||||
hw_type = self.get_type()
|
||||
if hw_type in PandaJungle.F4_DEVICES:
|
||||
return McuType.F4
|
||||
elif hw_type in PandaJungle.H7_DEVICES:
|
||||
return McuType.H7
|
||||
raise ValueError(f"unknown HW type: {hw_type}")
|
||||
|
||||
def up_to_date(self, fn=None) -> bool:
|
||||
if fn is None:
|
||||
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn.replace("panda", "panda_jungle"))
|
||||
return super().up_to_date(fn=fn)
|
||||
|
||||
# ******************* health *******************
|
||||
|
||||
@ensure_jungle_health_packet_version
|
||||
def health(self):
|
||||
dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xd2, 0, 0, self.HEALTH_STRUCT.size)
|
||||
a = self.HEALTH_STRUCT.unpack(dat)
|
||||
return {
|
||||
"uptime": a[0],
|
||||
"ch1_power": a[1],
|
||||
"ch2_power": a[2],
|
||||
"ch3_power": a[3],
|
||||
"ch4_power": a[4],
|
||||
"ch5_power": a[5],
|
||||
"ch6_power": a[6],
|
||||
"ch1_sbu1_voltage": a[7] / 1000.0,
|
||||
"ch1_sbu2_voltage": a[8] / 1000.0,
|
||||
"ch2_sbu1_voltage": a[9] / 1000.0,
|
||||
"ch2_sbu2_voltage": a[10] / 1000.0,
|
||||
"ch3_sbu1_voltage": a[11] / 1000.0,
|
||||
"ch3_sbu2_voltage": a[12] / 1000.0,
|
||||
"ch4_sbu1_voltage": a[13] / 1000.0,
|
||||
"ch4_sbu2_voltage": a[14] / 1000.0,
|
||||
"ch5_sbu1_voltage": a[15] / 1000.0,
|
||||
"ch5_sbu2_voltage": a[16] / 1000.0,
|
||||
"ch6_sbu1_voltage": a[17] / 1000.0,
|
||||
"ch6_sbu2_voltage": a[18] / 1000.0,
|
||||
}
|
||||
|
||||
# ******************* control *******************
|
||||
|
||||
# Returns tuple with health packet version and CAN packet/USB packet version
|
||||
def get_packets_versions(self):
|
||||
dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xdd, 0, 0, 3)
|
||||
if dat and len(dat) == 3:
|
||||
a = struct.unpack("BBB", dat)
|
||||
return (a[0], a[1], a[2])
|
||||
return (-1, -1, -1)
|
||||
|
||||
# ******************* jungle stuff *******************
|
||||
|
||||
def set_panda_power(self, enabled):
|
||||
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa0, int(enabled), 0, b'')
|
||||
|
||||
def set_panda_individual_power(self, port, enabled):
|
||||
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa3, int(port), int(enabled), b'')
|
||||
|
||||
def set_harness_orientation(self, mode):
|
||||
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa1, int(mode), 0, b'')
|
||||
|
||||
def set_ignition(self, enabled):
|
||||
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa2, int(enabled), 0, b'')
|
||||
|
||||
def set_can_silent(self, silent):
|
||||
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xf5, int(silent), 0, b'')
|
||||
|
||||
# ******************* serial *******************
|
||||
|
||||
def debug_read(self):
|
||||
ret = []
|
||||
while 1:
|
||||
lret = bytes(self._handle.controlRead(PandaJungle.REQUEST_IN, 0xe0, 0, 0, 0x40))
|
||||
if len(lret) == 0:
|
||||
break
|
||||
ret.append(lret)
|
||||
return b''.join(ret)
|
||||
|
||||
# ******************* header pins *******************
|
||||
|
||||
def set_header_pin(self, pin_num, enabled):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf7, int(pin_num), int(enabled), b'')
|
||||
84
panda/board/jungle/boards/board_declarations.h
Normal file
84
panda/board/jungle/boards/board_declarations.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// ******************** Prototypes ********************
|
||||
typedef void (*board_init)(void);
|
||||
typedef void (*board_set_led)(uint8_t color, bool enabled);
|
||||
typedef void (*board_board_tick)(void);
|
||||
typedef bool (*board_get_button)(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);
|
||||
typedef void (*board_set_individual_ignition)(uint8_t bitmask);
|
||||
typedef void (*board_set_harness_orientation)(uint8_t orientation);
|
||||
typedef void (*board_set_can_mode)(uint8_t mode);
|
||||
typedef void (*board_enable_can_transciever)(uint8_t transciever, bool enabled);
|
||||
typedef void (*board_enable_header_pin)(uint8_t pin_num, bool enabled);
|
||||
typedef float (*board_get_channel_power)(uint8_t channel);
|
||||
typedef uint16_t (*board_get_sbu_mV)(uint8_t channel, uint8_t sbu);
|
||||
|
||||
struct board {
|
||||
const char *board_type;
|
||||
const bool has_canfd;
|
||||
const bool has_sbu_sense;
|
||||
const uint16_t avdd_mV;
|
||||
board_init init;
|
||||
board_set_led set_led;
|
||||
board_board_tick board_tick;
|
||||
board_get_button get_button;
|
||||
board_set_panda_power set_panda_power;
|
||||
board_set_panda_individual_power set_panda_individual_power;
|
||||
board_set_ignition set_ignition;
|
||||
board_set_individual_ignition set_individual_ignition;
|
||||
board_set_harness_orientation set_harness_orientation;
|
||||
board_set_can_mode set_can_mode;
|
||||
board_enable_can_transciever enable_can_transciever;
|
||||
board_enable_header_pin enable_header_pin;
|
||||
board_get_channel_power get_channel_power;
|
||||
board_get_sbu_mV get_sbu_mV;
|
||||
|
||||
// TODO: shouldn't need these
|
||||
bool has_spi;
|
||||
bool has_hw_gmlan;
|
||||
};
|
||||
|
||||
// ******************* Definitions ********************
|
||||
#define HW_TYPE_UNKNOWN 0U
|
||||
#define HW_TYPE_V1 1U
|
||||
#define HW_TYPE_V2 2U
|
||||
|
||||
// LED colors
|
||||
#define LED_RED 0U
|
||||
#define LED_GREEN 1U
|
||||
#define LED_BLUE 2U
|
||||
|
||||
// CAN modes
|
||||
#define CAN_MODE_NORMAL 0U
|
||||
#define CAN_MODE_GMLAN_CAN2 1U
|
||||
#define CAN_MODE_GMLAN_CAN3 2U
|
||||
#define CAN_MODE_OBD_CAN2 3U
|
||||
|
||||
// Harness states
|
||||
#define HARNESS_ORIENTATION_NONE 0U
|
||||
#define HARNESS_ORIENTATION_1 1U
|
||||
#define HARNESS_ORIENTATION_2 2U
|
||||
|
||||
#define SBU1 0U
|
||||
#define SBU2 1U
|
||||
|
||||
// ********************* Globals **********************
|
||||
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);
|
||||
}
|
||||
176
panda/board/jungle/boards/board_v1.h
Normal file
176
panda/board/jungle/boards/board_v1.h
Normal file
@@ -0,0 +1,176 @@
|
||||
|
||||
void board_v1_set_led(uint8_t color, bool enabled) {
|
||||
switch (color) {
|
||||
case LED_RED:
|
||||
set_gpio_output(GPIOC, 9, !enabled);
|
||||
break;
|
||||
case LED_GREEN:
|
||||
set_gpio_output(GPIOC, 7, !enabled);
|
||||
break;
|
||||
case LED_BLUE:
|
||||
set_gpio_output(GPIOC, 6, !enabled);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void board_v1_enable_can_transciever(uint8_t transciever, bool enabled) {
|
||||
switch (transciever) {
|
||||
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 transciever ("); puth(transciever); print("): enabling failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void board_v1_set_can_mode(uint8_t mode) {
|
||||
board_v1_enable_can_transciever(2U, false);
|
||||
board_v1_enable_can_transciever(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_transciever(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_transciever(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 transcievers
|
||||
for(uint8_t i = 1; i <= 4; i++) {
|
||||
board_v1_enable_can_transciever(i, true);
|
||||
}
|
||||
|
||||
// Disable LEDs
|
||||
board_v1_set_led(LED_RED, false);
|
||||
board_v1_set_led(LED_GREEN, false);
|
||||
board_v1_set_led(LED_BLUE, false);
|
||||
|
||||
// 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) {}
|
||||
|
||||
const board board_v1 = {
|
||||
.board_type = "V1",
|
||||
.has_canfd = false,
|
||||
.has_sbu_sense = false,
|
||||
.avdd_mV = 3300U,
|
||||
.init = &board_v1_init,
|
||||
.set_led = &board_v1_set_led,
|
||||
.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_transciever = &board_v1_enable_can_transciever,
|
||||
.enable_header_pin = &unused_board_enable_header_pin,
|
||||
.get_channel_power = &board_v1_get_channel_power,
|
||||
.get_sbu_mV = &board_v1_get_sbu_mV,
|
||||
};
|
||||
326
panda/board/jungle/boards/board_v2.h
Normal file
326
panda/board/jungle/boards/board_v2.h
Normal file
@@ -0,0 +1,326 @@
|
||||
|
||||
const gpio_t power_pins[] = {
|
||||
{.bank = GPIOA, .pin = 0},
|
||||
{.bank = GPIOA, .pin = 1},
|
||||
{.bank = GPIOF, .pin = 12},
|
||||
{.bank = GPIOA, .pin = 5},
|
||||
{.bank = GPIOC, .pin = 5},
|
||||
{.bank = GPIOB, .pin = 2},
|
||||
};
|
||||
|
||||
const gpio_t sbu1_ignition_pins[] = {
|
||||
{.bank = GPIOD, .pin = 0},
|
||||
{.bank = GPIOD, .pin = 5},
|
||||
{.bank = GPIOD, .pin = 12},
|
||||
{.bank = GPIOD, .pin = 14},
|
||||
{.bank = GPIOE, .pin = 5},
|
||||
{.bank = GPIOE, .pin = 9},
|
||||
};
|
||||
|
||||
const gpio_t sbu1_relay_pins[] = {
|
||||
{.bank = GPIOD, .pin = 1},
|
||||
{.bank = GPIOD, .pin = 6},
|
||||
{.bank = GPIOD, .pin = 11},
|
||||
{.bank = GPIOD, .pin = 15},
|
||||
{.bank = GPIOE, .pin = 6},
|
||||
{.bank = GPIOE, .pin = 10},
|
||||
};
|
||||
|
||||
const gpio_t sbu2_ignition_pins[] = {
|
||||
{.bank = GPIOD, .pin = 3},
|
||||
{.bank = GPIOD, .pin = 8},
|
||||
{.bank = GPIOD, .pin = 9},
|
||||
{.bank = GPIOE, .pin = 0},
|
||||
{.bank = GPIOE, .pin = 7},
|
||||
{.bank = GPIOE, .pin = 11},
|
||||
};
|
||||
|
||||
const gpio_t sbu2_relay_pins[] = {
|
||||
{.bank = GPIOD, .pin = 4},
|
||||
{.bank = GPIOD, .pin = 10},
|
||||
{.bank = GPIOD, .pin = 13},
|
||||
{.bank = GPIOE, .pin = 1},
|
||||
{.bank = GPIOE, .pin = 8},
|
||||
{.bank = GPIOE, .pin = 12},
|
||||
};
|
||||
|
||||
const 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_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},
|
||||
};
|
||||
|
||||
void board_v2_set_led(uint8_t color, bool enabled) {
|
||||
switch (color) {
|
||||
case LED_RED:
|
||||
set_gpio_output(GPIOE, 4, !enabled);
|
||||
break;
|
||||
case LED_GREEN:
|
||||
set_gpio_output(GPIOE, 3, !enabled);
|
||||
break;
|
||||
case LED_BLUE:
|
||||
set_gpio_output(GPIOE, 2, !enabled);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void board_v2_set_harness_orientation(uint8_t orientation) {
|
||||
switch (orientation) {
|
||||
case HARNESS_ORIENTATION_NONE:
|
||||
gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false);
|
||||
gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false);
|
||||
gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false);
|
||||
gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false);
|
||||
harness_orientation = orientation;
|
||||
break;
|
||||
case HARNESS_ORIENTATION_1:
|
||||
gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false);
|
||||
gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), true);
|
||||
gpio_set_bitmask(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), ignition);
|
||||
gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false);
|
||||
harness_orientation = orientation;
|
||||
break;
|
||||
case HARNESS_ORIENTATION_2:
|
||||
gpio_set_bitmask(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), ignition);
|
||||
gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false);
|
||||
gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false);
|
||||
gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), true);
|
||||
harness_orientation = orientation;
|
||||
break;
|
||||
default:
|
||||
print("Tried to set an unsupported harness orientation: "); puth(orientation); print("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void board_v2_enable_can_transciever(uint8_t transciever, bool enabled) {
|
||||
switch (transciever) {
|
||||
case 1U:
|
||||
set_gpio_output(GPIOG, 11, !enabled);
|
||||
break;
|
||||
case 2U:
|
||||
set_gpio_output(GPIOB, 3, !enabled);
|
||||
break;
|
||||
case 3U:
|
||||
set_gpio_output(GPIOD, 7, !enabled);
|
||||
break;
|
||||
case 4U:
|
||||
set_gpio_output(GPIOB, 4, !enabled);
|
||||
break;
|
||||
default:
|
||||
print("Invalid CAN transciever ("); puth(transciever); print("): enabling failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void board_v2_enable_header_pin(uint8_t pin_num, bool enabled) {
|
||||
if (pin_num < 8U) {
|
||||
set_gpio_output(GPIOG, pin_num, enabled);
|
||||
} else {
|
||||
print("Invalid pin number ("); puth(pin_num); print("): enabling failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void board_v2_set_can_mode(uint8_t mode) {
|
||||
board_v2_enable_can_transciever(2U, false);
|
||||
board_v2_enable_can_transciever(4U, false);
|
||||
switch (mode) {
|
||||
case CAN_MODE_NORMAL:
|
||||
// B12,B13: disable normal mode
|
||||
set_gpio_pullup(GPIOB, 12, PULL_NONE);
|
||||
set_gpio_mode(GPIOB, 12, MODE_ANALOG);
|
||||
|
||||
set_gpio_pullup(GPIOB, 13, PULL_NONE);
|
||||
set_gpio_mode(GPIOB, 13, MODE_ANALOG);
|
||||
|
||||
// B5,B6: FDCAN2 mode
|
||||
set_gpio_pullup(GPIOB, 5, PULL_NONE);
|
||||
set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2);
|
||||
|
||||
set_gpio_pullup(GPIOB, 6, PULL_NONE);
|
||||
set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2);
|
||||
can_mode = CAN_MODE_NORMAL;
|
||||
board_v2_enable_can_transciever(2U, true);
|
||||
break;
|
||||
case CAN_MODE_OBD_CAN2:
|
||||
// B5,B6: disable normal mode
|
||||
set_gpio_pullup(GPIOB, 5, PULL_NONE);
|
||||
set_gpio_mode(GPIOB, 5, MODE_ANALOG);
|
||||
|
||||
set_gpio_pullup(GPIOB, 6, PULL_NONE);
|
||||
set_gpio_mode(GPIOB, 6, MODE_ANALOG);
|
||||
// B12,B13: FDCAN2 mode
|
||||
set_gpio_pullup(GPIOB, 12, PULL_NONE);
|
||||
set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2);
|
||||
|
||||
set_gpio_pullup(GPIOB, 13, PULL_NONE);
|
||||
set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2);
|
||||
can_mode = CAN_MODE_OBD_CAN2;
|
||||
board_v2_enable_can_transciever(4U, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool panda_power = false;
|
||||
uint8_t panda_power_bitmask = 0U;
|
||||
void board_v2_set_panda_power(bool enable) {
|
||||
panda_power = enable;
|
||||
gpio_set_all_output(power_pins, sizeof(power_pins) / sizeof(gpio_t), enable);
|
||||
if (enable) {
|
||||
panda_power_bitmask = 0xFFU;
|
||||
} else {
|
||||
panda_power_bitmask = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void board_v2_set_panda_individual_power(uint8_t port_num, bool enable) {
|
||||
port_num -= 1U;
|
||||
if (port_num < 6U) {
|
||||
panda_power_bitmask &= ~(1U << port_num);
|
||||
panda_power_bitmask |= (enable ? 1U : 0U) << port_num;
|
||||
} else {
|
||||
print("Invalid port number ("); puth(port_num); print("): enabling failed\n");
|
||||
}
|
||||
gpio_set_bitmask(power_pins, sizeof(power_pins) / sizeof(gpio_t), (uint32_t)panda_power_bitmask);
|
||||
}
|
||||
|
||||
bool board_v2_get_button(void) {
|
||||
return get_gpio_input(GPIOG, 15);
|
||||
}
|
||||
|
||||
void board_v2_set_ignition(bool enabled) {
|
||||
ignition = enabled ? 0xFFU : 0U;
|
||||
board_v2_set_harness_orientation(harness_orientation);
|
||||
}
|
||||
|
||||
void board_v2_set_individual_ignition(uint8_t bitmask) {
|
||||
ignition = bitmask;
|
||||
board_v2_set_harness_orientation(harness_orientation);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
ret = (((float) readout / 33e6) - 0.8e-6) / 52e-6 * 12.0f;
|
||||
} else {
|
||||
print("Invalid channel ("); puth(channel); print(")\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t board_v2_get_sbu_mV(uint8_t channel, uint8_t sbu) {
|
||||
uint16_t ret = 0U;
|
||||
if ((channel >= 1U) && (channel <= 6U)) {
|
||||
switch(sbu){
|
||||
case SBU1:
|
||||
ret = adc_get_mV(sbu1_channels[channel - 1U].adc, sbu1_channels[channel - 1U].channel);
|
||||
break;
|
||||
case SBU2:
|
||||
ret = adc_get_mV(sbu2_channels[channel - 1U].adc, sbu2_channels[channel - 1U].channel);
|
||||
break;
|
||||
default:
|
||||
print("Invalid SBU ("); puth(sbu); print(")\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
print("Invalid channel ("); puth(channel); print(")\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void board_v2_init(void) {
|
||||
common_init_gpio();
|
||||
|
||||
// Disable LEDs
|
||||
board_v2_set_led(LED_RED, false);
|
||||
board_v2_set_led(LED_GREEN, false);
|
||||
board_v2_set_led(LED_BLUE, false);
|
||||
|
||||
// Normal CAN mode
|
||||
board_v2_set_can_mode(CAN_MODE_NORMAL);
|
||||
|
||||
// Enable CAN transcievers
|
||||
for(uint8_t i = 1; i <= 4; i++) {
|
||||
board_v2_enable_can_transciever(i, true);
|
||||
}
|
||||
|
||||
// Set to no harness orientation
|
||||
board_v2_set_harness_orientation(HARNESS_ORIENTATION_NONE);
|
||||
|
||||
// Enable panda power by default
|
||||
board_v2_set_panda_power(true);
|
||||
|
||||
// Current monitor channels
|
||||
adc_init(ADC1);
|
||||
register_set_bits(&SYSCFG->PMCR, SYSCFG_PMCR_PA0SO | SYSCFG_PMCR_PA1SO); // open up analog switches for PA0_C and PA1_C
|
||||
set_gpio_mode(GPIOF, 11, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOA, 6, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOC, 4, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOB, 1, MODE_ANALOG);
|
||||
|
||||
// SBU channels
|
||||
adc_init(ADC3);
|
||||
set_gpio_mode(GPIOC, 2, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOC, 3, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 9, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 7, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 5, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 3, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 10, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 8, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 6, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOF, 4, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOC, 0, MODE_ANALOG);
|
||||
set_gpio_mode(GPIOC, 1, MODE_ANALOG);
|
||||
|
||||
// Header pins
|
||||
set_gpio_mode(GPIOG, 0, MODE_OUTPUT);
|
||||
set_gpio_mode(GPIOG, 1, MODE_OUTPUT);
|
||||
set_gpio_mode(GPIOG, 2, MODE_OUTPUT);
|
||||
set_gpio_mode(GPIOG, 3, MODE_OUTPUT);
|
||||
set_gpio_mode(GPIOG, 4, MODE_OUTPUT);
|
||||
set_gpio_mode(GPIOG, 5, MODE_OUTPUT);
|
||||
set_gpio_mode(GPIOG, 6, MODE_OUTPUT);
|
||||
set_gpio_mode(GPIOG, 7, MODE_OUTPUT);
|
||||
}
|
||||
|
||||
void board_v2_tick(void) {}
|
||||
|
||||
const board board_v2 = {
|
||||
.board_type = "V2",
|
||||
.has_canfd = true,
|
||||
.has_sbu_sense = true,
|
||||
.avdd_mV = 3300U,
|
||||
.init = &board_v2_init,
|
||||
.set_led = &board_v2_set_led,
|
||||
.board_tick = &board_v2_tick,
|
||||
.get_button = &board_v2_get_button,
|
||||
.set_panda_power = &board_v2_set_panda_power,
|
||||
.set_panda_individual_power = &board_v2_set_panda_individual_power,
|
||||
.set_ignition = &board_v2_set_ignition,
|
||||
.set_individual_ignition = &board_v2_set_individual_ignition,
|
||||
.set_harness_orientation = &board_v2_set_harness_orientation,
|
||||
.set_can_mode = &board_v2_set_can_mode,
|
||||
.enable_can_transciever = &board_v2_enable_can_transciever,
|
||||
.enable_header_pin = &board_v2_enable_header_pin,
|
||||
.get_channel_power = &board_v2_get_channel_power,
|
||||
.get_sbu_mV = &board_v2_get_sbu_mV,
|
||||
};
|
||||
17
panda/board/jungle/flash.py
Executable file
17
panda/board/jungle/flash.py
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from panda import PandaJungle
|
||||
|
||||
board_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
if __name__ == "__main__":
|
||||
subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True)
|
||||
|
||||
serials = PandaJungle.list()
|
||||
print(f"found {len(serials)} panda jungle(s) - {serials}")
|
||||
for s in serials:
|
||||
print("flashing", s)
|
||||
with PandaJungle(serial=s) as p:
|
||||
p.flash()
|
||||
24
panda/board/jungle/jungle_health.h
Normal file
24
panda/board/jungle/jungle_health.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// When changing these structs, python/__init__.py needs to be kept up to date!
|
||||
|
||||
#define JUNGLE_HEALTH_PACKET_VERSION 1
|
||||
struct __attribute__((packed)) jungle_health_t {
|
||||
uint32_t uptime_pkt;
|
||||
float ch1_power;
|
||||
float ch2_power;
|
||||
float ch3_power;
|
||||
float ch4_power;
|
||||
float ch5_power;
|
||||
float ch6_power;
|
||||
uint16_t ch1_sbu1_mV;
|
||||
uint16_t ch1_sbu2_mV;
|
||||
uint16_t ch2_sbu1_mV;
|
||||
uint16_t ch2_sbu2_mV;
|
||||
uint16_t ch3_sbu1_mV;
|
||||
uint16_t ch3_sbu2_mV;
|
||||
uint16_t ch4_sbu1_mV;
|
||||
uint16_t ch4_sbu2_mV;
|
||||
uint16_t ch5_sbu1_mV;
|
||||
uint16_t ch5_sbu2_mV;
|
||||
uint16_t ch6_sbu1_mV;
|
||||
uint16_t ch6_sbu2_mV;
|
||||
};
|
||||
216
panda/board/jungle/main.c
Normal file
216
panda/board/jungle/main.c
Normal file
@@ -0,0 +1,216 @@
|
||||
// ********************* Includes *********************
|
||||
#include "board/config.h"
|
||||
|
||||
#include "board/safety.h"
|
||||
#include "board/drivers/gmlan_alt.h"
|
||||
|
||||
#include "board/drivers/pwm.h"
|
||||
#include "board/drivers/usb.h"
|
||||
|
||||
#include "board/early_init.h"
|
||||
#include "board/provision.h"
|
||||
|
||||
#include "board/health.h"
|
||||
#include "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/obj/gitversion.h"
|
||||
|
||||
#include "board/can_comms.h"
|
||||
#include "main_comms.h"
|
||||
|
||||
|
||||
// ********************* Serial debugging *********************
|
||||
|
||||
void debug_ring_callback(uart_ring *ring) {
|
||||
char rcv;
|
||||
while (getc(ring, &rcv)) {
|
||||
(void)injectc(ring, rcv);
|
||||
}
|
||||
}
|
||||
|
||||
// ***************************** main code *****************************
|
||||
|
||||
// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck
|
||||
void __initialize_hardware_early(void) {
|
||||
early_initialization();
|
||||
}
|
||||
|
||||
void __attribute__ ((noinline)) enable_fpu(void) {
|
||||
// enable the FPU
|
||||
SCB->CPACR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U)));
|
||||
}
|
||||
|
||||
// called at 8Hz
|
||||
uint32_t loop_counter = 0U;
|
||||
uint16_t button_press_cnt = 0U;
|
||||
void tick_handler(void) {
|
||||
if (TICK_TIMER->SR != 0) {
|
||||
// tick drivers at 8Hz
|
||||
usb_tick();
|
||||
|
||||
// decimated to 1Hz
|
||||
if ((loop_counter % 8) == 0U) {
|
||||
#ifdef DEBUG
|
||||
print("** blink ");
|
||||
print("rx:"); puth4(can_rx_q.r_ptr); print("-"); puth4(can_rx_q.w_ptr); print(" ");
|
||||
print("tx1:"); puth4(can_tx1_q.r_ptr); print("-"); puth4(can_tx1_q.w_ptr); print(" ");
|
||||
print("tx2:"); puth4(can_tx2_q.r_ptr); print("-"); puth4(can_tx2_q.w_ptr); print(" ");
|
||||
print("tx3:"); puth4(can_tx3_q.r_ptr); print("-"); puth4(can_tx3_q.w_ptr); print("\n");
|
||||
#endif
|
||||
|
||||
current_board->board_tick();
|
||||
|
||||
// check registers
|
||||
check_registers();
|
||||
|
||||
// turn off the blue LED, turned on by CAN
|
||||
current_board->set_led(LED_BLUE, false);
|
||||
|
||||
// Blink and OBD CAN
|
||||
#ifdef FINAL_PROVISIONING
|
||||
current_board->set_can_mode(can_mode == CAN_MODE_NORMAL ? CAN_MODE_OBD_CAN2 : CAN_MODE_NORMAL);
|
||||
#endif
|
||||
|
||||
// on to the next one
|
||||
uptime_cnt += 1U;
|
||||
}
|
||||
|
||||
current_board->set_led(LED_GREEN, green_led_enabled);
|
||||
|
||||
// Check on button
|
||||
bool current_button_status = current_board->get_button();
|
||||
|
||||
if (current_button_status && button_press_cnt == 10) {
|
||||
current_board->set_panda_power(!panda_power);
|
||||
}
|
||||
|
||||
#ifdef FINAL_PROVISIONING
|
||||
// Ignition blinking
|
||||
uint8_t ignition_bitmask = 0U;
|
||||
for (uint8_t i = 0U; i < 6U; i++) {
|
||||
ignition_bitmask |= ((loop_counter % 12U) < ((uint32_t) i + 2U)) << i;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// toggle ignition on button press
|
||||
static bool prev_button_status = false;
|
||||
if (!current_button_status && prev_button_status && button_press_cnt < 10){
|
||||
current_board->set_ignition(!ignition);
|
||||
}
|
||||
prev_button_status = current_button_status;
|
||||
#endif
|
||||
|
||||
button_press_cnt = current_button_status ? button_press_cnt + 1 : 0;
|
||||
|
||||
loop_counter++;
|
||||
}
|
||||
TICK_TIMER->SR = 0;
|
||||
}
|
||||
|
||||
|
||||
int main(void) {
|
||||
// Init interrupt table
|
||||
init_interrupts(true);
|
||||
|
||||
// shouldn't have interrupts here, but just in case
|
||||
disable_interrupts();
|
||||
|
||||
// init early devices
|
||||
clock_init();
|
||||
peripherals_init();
|
||||
detect_board_type();
|
||||
// red+green leds enabled until succesful USB init, as a debug indicator
|
||||
current_board->set_led(LED_RED, true);
|
||||
current_board->set_led(LED_GREEN, true);
|
||||
|
||||
// print hello
|
||||
print("\n\n\n************************ MAIN START ************************\n");
|
||||
|
||||
// check for non-supported board types
|
||||
if (hw_type == HW_TYPE_UNKNOWN) {
|
||||
print("Unsupported board type\n");
|
||||
while (1) { /* hang */ }
|
||||
}
|
||||
|
||||
print("Config:\n");
|
||||
print(" Board type: "); print(current_board->board_type); print("\n");
|
||||
|
||||
// init board
|
||||
current_board->init();
|
||||
|
||||
// we have an FPU, let's use it!
|
||||
enable_fpu();
|
||||
|
||||
microsecond_timer_init();
|
||||
|
||||
// 8Hz timer
|
||||
REGISTER_INTERRUPT(TICK_TIMER_IRQ, tick_handler, 10U, FAULT_INTERRUPT_RATE_TICK)
|
||||
tick_timer_init();
|
||||
|
||||
#ifdef DEBUG
|
||||
print("DEBUG ENABLED\n");
|
||||
#endif
|
||||
// enable USB (right before interrupts or enum can fail!)
|
||||
usb_init();
|
||||
|
||||
current_board->set_led(LED_RED, false);
|
||||
current_board->set_led(LED_GREEN, false);
|
||||
|
||||
print("**** INTERRUPTS ON ****\n");
|
||||
enable_interrupts();
|
||||
|
||||
can_silent = ALL_CAN_LIVE;
|
||||
set_safety_hooks(SAFETY_ALLOUTPUT, 0U);
|
||||
|
||||
can_init_all();
|
||||
current_board->set_harness_orientation(HARNESS_ORIENTATION_1);
|
||||
|
||||
#ifdef FINAL_PROVISIONING
|
||||
print("---- FINAL PROVISIONING BUILD ---- \n");
|
||||
can_set_forwarding(0, 2);
|
||||
can_set_forwarding(1, 2);
|
||||
#endif
|
||||
|
||||
// LED should keep on blinking all the time
|
||||
uint64_t cnt = 0;
|
||||
for (cnt=0;;cnt++) {
|
||||
// useful for debugging, fade breaks = panda is overloaded
|
||||
for (uint32_t fade = 0U; fade < MAX_LED_FADE; fade += 1U) {
|
||||
current_board->set_led(LED_RED, true);
|
||||
delay(fade >> 4);
|
||||
current_board->set_led(LED_RED, false);
|
||||
delay((MAX_LED_FADE - fade) >> 4);
|
||||
}
|
||||
|
||||
for (uint32_t fade = MAX_LED_FADE; fade > 0U; fade -= 1U) {
|
||||
current_board->set_led(LED_RED, true);
|
||||
delay(fade >> 4);
|
||||
current_board->set_led(LED_RED, false);
|
||||
delay((MAX_LED_FADE - fade) >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
261
panda/board/jungle/main_comms.h
Normal file
261
panda/board/jungle/main_comms.h
Normal file
@@ -0,0 +1,261 @@
|
||||
extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used
|
||||
|
||||
int get_jungle_health_pkt(void *dat) {
|
||||
COMPILE_TIME_ASSERT(sizeof(struct jungle_health_t) <= USBPACKET_MAX_SIZE);
|
||||
struct jungle_health_t * health = (struct jungle_health_t*)dat;
|
||||
|
||||
health->uptime_pkt = uptime_cnt;
|
||||
health->ch1_power = current_board->get_channel_power(1U);
|
||||
health->ch2_power = current_board->get_channel_power(2U);
|
||||
health->ch3_power = current_board->get_channel_power(3U);
|
||||
health->ch4_power = current_board->get_channel_power(4U);
|
||||
health->ch5_power = current_board->get_channel_power(5U);
|
||||
health->ch6_power = current_board->get_channel_power(6U);
|
||||
|
||||
health->ch1_sbu1_mV = current_board->get_sbu_mV(1U, SBU1);
|
||||
health->ch1_sbu2_mV = current_board->get_sbu_mV(1U, SBU2);
|
||||
health->ch2_sbu1_mV = current_board->get_sbu_mV(2U, SBU1);
|
||||
health->ch2_sbu2_mV = current_board->get_sbu_mV(2U, SBU2);
|
||||
health->ch3_sbu1_mV = current_board->get_sbu_mV(3U, SBU1);
|
||||
health->ch3_sbu2_mV = current_board->get_sbu_mV(3U, SBU2);
|
||||
health->ch4_sbu1_mV = current_board->get_sbu_mV(4U, SBU1);
|
||||
health->ch4_sbu2_mV = current_board->get_sbu_mV(4U, SBU2);
|
||||
health->ch5_sbu1_mV = current_board->get_sbu_mV(5U, SBU1);
|
||||
health->ch5_sbu2_mV = current_board->get_sbu_mV(5U, SBU2);
|
||||
health->ch6_sbu1_mV = current_board->get_sbu_mV(6U, SBU1);
|
||||
health->ch6_sbu2_mV = current_board->get_sbu_mV(6U, SBU2);
|
||||
|
||||
return sizeof(*health);
|
||||
}
|
||||
|
||||
// send on serial, first byte to select the ring
|
||||
void comms_endpoint2_write(uint8_t *data, uint32_t len) {
|
||||
UNUSED(data);
|
||||
UNUSED(len);
|
||||
}
|
||||
|
||||
int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
|
||||
unsigned int resp_len = 0;
|
||||
uint32_t time;
|
||||
|
||||
#ifdef DEBUG_COMMS
|
||||
print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n");
|
||||
print("- request "); puth(req->request); print("\n");
|
||||
print("- param1 "); puth(req->param1); print("\n");
|
||||
print("- param2 "); puth(req->param2); print("\n");
|
||||
#endif
|
||||
|
||||
switch (req->request) {
|
||||
// **** 0xa0: Set panda power.
|
||||
case 0xa0:
|
||||
current_board->set_panda_power((req->param1 == 1U));
|
||||
break;
|
||||
// **** 0xa1: Set harness orientation.
|
||||
case 0xa1:
|
||||
current_board->set_harness_orientation(req->param1);
|
||||
break;
|
||||
// **** 0xa2: Set ignition.
|
||||
case 0xa2:
|
||||
current_board->set_ignition((req->param1 == 1U));
|
||||
break;
|
||||
// **** 0xa0: Set panda power per channel by bitmask.
|
||||
case 0xa3:
|
||||
current_board->set_panda_individual_power(req->param1, (req->param2 > 0U));
|
||||
break;
|
||||
// **** 0xa8: get microsecond timer
|
||||
case 0xa8:
|
||||
time = microsecond_timer_get();
|
||||
resp[0] = (time & 0x000000FFU);
|
||||
resp[1] = ((time & 0x0000FF00U) >> 8U);
|
||||
resp[2] = ((time & 0x00FF0000U) >> 16U);
|
||||
resp[3] = ((time & 0xFF000000U) >> 24U);
|
||||
resp_len = 4U;
|
||||
break;
|
||||
// **** 0xc0: reset communications
|
||||
case 0xc0:
|
||||
comms_can_reset();
|
||||
break;
|
||||
// **** 0xc1: get hardware type
|
||||
case 0xc1:
|
||||
resp[0] = hw_type;
|
||||
resp_len = 1;
|
||||
break;
|
||||
// **** 0xc2: CAN health stats
|
||||
case 0xc2:
|
||||
COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE);
|
||||
if (req->param1 < 3U) {
|
||||
update_can_health_pkt(req->param1, 0U);
|
||||
can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U);
|
||||
can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U);
|
||||
can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled;
|
||||
can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled;
|
||||
can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso;
|
||||
resp_len = sizeof(can_health[req->param1]);
|
||||
(void)memcpy(resp, &can_health[req->param1], resp_len);
|
||||
}
|
||||
break;
|
||||
// **** 0xc3: fetch MCU UID
|
||||
case 0xc3:
|
||||
(void)memcpy(resp, ((uint8_t *)UID_BASE), 12);
|
||||
resp_len = 12;
|
||||
break;
|
||||
// **** 0xd0: fetch serial (aka the provisioned dongle ID)
|
||||
case 0xd0:
|
||||
// addresses are OTP
|
||||
if (req->param1 == 1U) {
|
||||
(void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10);
|
||||
resp_len = 0x10;
|
||||
} else {
|
||||
get_provision_chunk(resp);
|
||||
resp_len = PROVISION_CHUNK_LEN;
|
||||
}
|
||||
break;
|
||||
// **** 0xd1: enter bootloader mode
|
||||
case 0xd1:
|
||||
// this allows reflashing of the bootstub
|
||||
switch (req->param1) {
|
||||
case 0:
|
||||
// only allow bootloader entry on debug builds
|
||||
#ifdef ALLOW_DEBUG
|
||||
print("-> entering bootloader\n");
|
||||
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
|
||||
NVIC_SystemReset();
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
print("-> entering softloader\n");
|
||||
enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC;
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
default:
|
||||
print("Bootloader mode invalid\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// **** 0xd2: get health packet
|
||||
case 0xd2:
|
||||
resp_len = get_jungle_health_pkt(resp);
|
||||
break;
|
||||
// **** 0xd3: get first 64 bytes of signature
|
||||
case 0xd3:
|
||||
{
|
||||
resp_len = 64;
|
||||
char * code = (char*)_app_start;
|
||||
int code_len = _app_start[0];
|
||||
(void)memcpy(resp, &code[code_len], resp_len);
|
||||
}
|
||||
break;
|
||||
// **** 0xd4: get second 64 bytes of signature
|
||||
case 0xd4:
|
||||
{
|
||||
resp_len = 64;
|
||||
char * code = (char*)_app_start;
|
||||
int code_len = _app_start[0];
|
||||
(void)memcpy(resp, &code[code_len + 64], resp_len);
|
||||
}
|
||||
break;
|
||||
// **** 0xd6: get version
|
||||
case 0xd6:
|
||||
COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE);
|
||||
(void)memcpy(resp, gitversion, sizeof(gitversion));
|
||||
resp_len = sizeof(gitversion) - 1U;
|
||||
break;
|
||||
// **** 0xd8: reset ST
|
||||
case 0xd8:
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
// **** 0xdb: set OBD CAN multiplexing mode
|
||||
case 0xdb:
|
||||
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;
|
||||
// **** 0xdd: get healthpacket and CANPacket versions
|
||||
case 0xdd:
|
||||
resp[0] = JUNGLE_HEALTH_PACKET_VERSION;
|
||||
resp[1] = CAN_PACKET_VERSION;
|
||||
resp[2] = CAN_HEALTH_PACKET_VERSION;
|
||||
resp_len = 3;
|
||||
break;
|
||||
// **** 0xde: set can bitrate
|
||||
case 0xde:
|
||||
if ((req->param1 < PANDA_BUS_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) {
|
||||
bus_config[req->param1].can_speed = req->param2;
|
||||
bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1));
|
||||
UNUSED(ret);
|
||||
}
|
||||
break;
|
||||
// **** 0xe0: debug read
|
||||
case 0xe0:
|
||||
// read
|
||||
while ((resp_len < MIN(req->length, USBPACKET_MAX_SIZE)) && getc(get_ring_by_number(0), (char*)&resp[resp_len])) {
|
||||
++resp_len;
|
||||
}
|
||||
break;
|
||||
// **** 0xe5: set CAN loopback (for testing)
|
||||
case 0xe5:
|
||||
can_loopback = (req->param1 > 0U);
|
||||
can_init_all();
|
||||
break;
|
||||
// **** 0xf1: Clear CAN ring buffer.
|
||||
case 0xf1:
|
||||
if (req->param1 == 0xFFFFU) {
|
||||
print("Clearing CAN Rx queue\n");
|
||||
can_clear(&can_rx_q);
|
||||
} else if (req->param1 < PANDA_BUS_CNT) {
|
||||
print("Clearing CAN Tx queue\n");
|
||||
can_clear(can_queues[req->param1]);
|
||||
} else {
|
||||
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_transciever(req->param1, req->param2 > 0U);
|
||||
break;
|
||||
// **** 0xf5: Set CAN silent mode
|
||||
case 0xf5:
|
||||
can_silent = (req->param1 > 0U) ? ALL_CAN_SILENT : ALL_CAN_LIVE;
|
||||
can_init_all();
|
||||
break;
|
||||
// **** 0xf7: enable/disable header pin by number
|
||||
case 0xf7:
|
||||
current_board->enable_header_pin(req->param1, req->param2 > 0U);
|
||||
break;
|
||||
// **** 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);
|
||||
bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed);
|
||||
bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1));
|
||||
UNUSED(ret);
|
||||
}
|
||||
break;
|
||||
// **** 0xfc: set CAN FD non-ISO mode
|
||||
case 0xfc:
|
||||
if ((req->param1 < PANDA_CAN_CNT) && current_board->has_canfd) {
|
||||
bus_config[req->param1].canfd_non_iso = (req->param2 != 0U);
|
||||
bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1));
|
||||
UNUSED(ret);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print("NO HANDLER ");
|
||||
puth(req->request);
|
||||
print("\n");
|
||||
break;
|
||||
}
|
||||
return resp_len;
|
||||
}
|
||||
26
panda/board/jungle/recover.py
Executable file
26
panda/board/jungle/recover.py
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
from panda import PandaJungle, PandaJungleDFU
|
||||
|
||||
board_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
if __name__ == "__main__":
|
||||
subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True)
|
||||
|
||||
for s in PandaJungle.list():
|
||||
print("putting", s, "in DFU mode")
|
||||
with PandaJungle(serial=s) as p:
|
||||
p.reset(enter_bootstub=True)
|
||||
p.reset(enter_bootloader=True)
|
||||
|
||||
# wait for reset panda jungles to come back up
|
||||
time.sleep(1)
|
||||
|
||||
dfu_serials = PandaJungleDFU.list()
|
||||
print(f"found {len(dfu_serials)} panda jungle(s) in DFU - {dfu_serials}")
|
||||
for s in dfu_serials:
|
||||
print("flashing", s)
|
||||
PandaJungleDFU(s).recover()
|
||||
13
panda/board/jungle/scripts/can_health.py
Executable file
13
panda/board/jungle/scripts/can_health.py
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import time
|
||||
from panda import PandaJungle
|
||||
|
||||
if __name__ == "__main__":
|
||||
jungle = PandaJungle()
|
||||
|
||||
while True:
|
||||
for bus in range(3):
|
||||
print(bus, jungle.can_health(bus))
|
||||
print()
|
||||
time.sleep(1)
|
||||
35
panda/board/jungle/scripts/can_printer.py
Executable file
35
panda/board/jungle/scripts/can_printer.py
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
import binascii
|
||||
|
||||
from panda import PandaJungle
|
||||
|
||||
# fake
|
||||
def sec_since_boot():
|
||||
return time.time()
|
||||
|
||||
def can_printer():
|
||||
p = PandaJungle()
|
||||
|
||||
start = sec_since_boot()
|
||||
lp = sec_since_boot()
|
||||
msgs = defaultdict(list)
|
||||
canbus = int(os.getenv("CAN", "0"))
|
||||
while True:
|
||||
can_recv = p.can_recv()
|
||||
for address, _, dat, src in can_recv:
|
||||
if src == canbus:
|
||||
msgs[address].append(dat)
|
||||
|
||||
if sec_since_boot() - lp > 0.1:
|
||||
dd = chr(27) + "[2J"
|
||||
dd += "%5.2f\n" % (sec_since_boot() - start)
|
||||
for k,v in sorted(zip(list(msgs.keys()), [binascii.hexlify(x[-1]) for x in list(msgs.values())], strict=True)):
|
||||
dd += "%s(%6d) %s\n" % ("%04X(%4d)" % (k,k),len(msgs[k]), v)
|
||||
print(dd)
|
||||
lp = sec_since_boot()
|
||||
|
||||
if __name__ == "__main__":
|
||||
can_printer()
|
||||
38
panda/board/jungle/scripts/debug_console.py
Executable file
38
panda/board/jungle/scripts/debug_console.py
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from panda import PandaJungle
|
||||
|
||||
setcolor = ["\033[1;32;40m", "\033[1;31;40m"]
|
||||
unsetcolor = "\033[00m"
|
||||
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
try:
|
||||
claim = os.getenv("CLAIM") is not None
|
||||
|
||||
serials = PandaJungle.list()
|
||||
if os.getenv("SERIAL"):
|
||||
serials = [x for x in serials if x==os.getenv("SERIAL")]
|
||||
|
||||
panda_jungles = [PandaJungle(x, claim=claim) for x in serials]
|
||||
|
||||
if not len(panda_jungles):
|
||||
sys.exit("no panda jungles found")
|
||||
|
||||
while True:
|
||||
for i, panda_jungle in enumerate(panda_jungles):
|
||||
while True:
|
||||
ret = panda_jungle.debug_read()
|
||||
if len(ret) > 0:
|
||||
sys.stdout.write(setcolor[i] + ret.decode('ascii') + unsetcolor)
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
break
|
||||
time.sleep(0.01)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("panda jungle disconnected!")
|
||||
time.sleep(0.5)
|
||||
68
panda/board/jungle/scripts/echo_loopback_test.py
Executable file
68
panda/board/jungle/scripts/echo_loopback_test.py
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
import contextlib
|
||||
import random
|
||||
from termcolor import cprint
|
||||
|
||||
from panda import PandaJungle
|
||||
|
||||
# This script is intended to be used in conjunction with the echo.py test script from panda.
|
||||
# It sends messages on bus 0, 1, 2 and checks for a reversed response to be sent back.
|
||||
|
||||
#################################################################
|
||||
############################# UTILS #############################
|
||||
#################################################################
|
||||
def print_colored(text, color):
|
||||
cprint(text + " "*40, color, end="\r")
|
||||
|
||||
def get_test_string():
|
||||
return b"test"+os.urandom(4)
|
||||
|
||||
#################################################################
|
||||
############################# TEST ##############################
|
||||
#################################################################
|
||||
|
||||
def test_loopback():
|
||||
for bus in range(3):
|
||||
# Clear can
|
||||
jungle.can_clear(bus)
|
||||
# Send a random message
|
||||
address = random.randint(1, 2000)
|
||||
data = get_test_string()
|
||||
jungle.can_send(address, data, bus)
|
||||
time.sleep(0.1)
|
||||
|
||||
# Make sure it comes back reversed
|
||||
incoming = jungle.can_recv()
|
||||
found = False
|
||||
for message in incoming:
|
||||
incomingAddress, _, incomingData, incomingBus = message
|
||||
if incomingAddress == address and incomingData == data[::-1] and incomingBus == bus:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
cprint("\nFAILED", "red")
|
||||
raise AssertionError
|
||||
|
||||
#################################################################
|
||||
############################# MAIN ##############################
|
||||
#################################################################
|
||||
jungle = None
|
||||
counter = 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Connect to jungle silently
|
||||
print_colored("Connecting to jungle", "blue")
|
||||
with open(os.devnull, "w") as devnull:
|
||||
with contextlib.redirect_stdout(devnull):
|
||||
jungle = PandaJungle()
|
||||
jungle.set_panda_power(True)
|
||||
jungle.set_ignition(False)
|
||||
|
||||
# Run test
|
||||
while True:
|
||||
jungle.can_clear(0xFFFF)
|
||||
test_loopback()
|
||||
counter += 1
|
||||
print_colored(str(counter) + " loopback cycles complete", "blue")
|
||||
9
panda/board/jungle/scripts/get_version.py
Executable file
9
panda/board/jungle/scripts/get_version.py
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
from panda import PandaJungle
|
||||
|
||||
if __name__ == "__main__":
|
||||
for p in PandaJungle.list():
|
||||
pp = PandaJungle(p)
|
||||
print("%s: %s" % (pp.get_serial()[0], pp.get_version()))
|
||||
|
||||
|
||||
21
panda/board/jungle/scripts/health_test.py
Executable file
21
panda/board/jungle/scripts/health_test.py
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
from pprint import pprint
|
||||
|
||||
from panda import PandaJungle
|
||||
|
||||
if __name__ == "__main__":
|
||||
i = 0
|
||||
pi = 0
|
||||
|
||||
pj = PandaJungle()
|
||||
while True:
|
||||
st = time.monotonic()
|
||||
while time.monotonic() - st < 1:
|
||||
pj.health()
|
||||
pj.can_health(0)
|
||||
i += 1
|
||||
pprint(pj.health())
|
||||
print(f"Speed: {i - pi}Hz")
|
||||
pi = i
|
||||
|
||||
140
panda/board/jungle/scripts/loopback_test.py
Executable file
140
panda/board/jungle/scripts/loopback_test.py
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
import contextlib
|
||||
import random
|
||||
from termcolor import cprint
|
||||
|
||||
from panda import Panda, PandaJungle
|
||||
|
||||
NUM_PANDAS_PER_TEST = 1
|
||||
FOR_RELEASE_BUILDS = os.getenv("RELEASE") is not None # Release builds do not have ALLOUTPUT mode
|
||||
|
||||
BUS_SPEEDS = [125, 500, 1000]
|
||||
|
||||
#################################################################
|
||||
############################# UTILS #############################
|
||||
#################################################################
|
||||
# To suppress the connection text
|
||||
def silent_panda_connect(serial):
|
||||
with open(os.devnull, "w") as devnull:
|
||||
with contextlib.redirect_stdout(devnull):
|
||||
panda = Panda(serial)
|
||||
return panda
|
||||
|
||||
def print_colored(text, color):
|
||||
cprint(text + " "*40, color, end="\r")
|
||||
|
||||
def connect_to_pandas():
|
||||
print_colored("Connecting to pandas", "blue")
|
||||
# Connect to pandas
|
||||
pandas = []
|
||||
for serial in panda_serials:
|
||||
pandas.append(silent_panda_connect(serial))
|
||||
print_colored("Connected", "blue")
|
||||
|
||||
def start_with_orientation(orientation):
|
||||
print_colored("Restarting pandas with orientation " + str(orientation), "blue")
|
||||
jungle.set_panda_power(False)
|
||||
jungle.set_harness_orientation(orientation)
|
||||
time.sleep(4)
|
||||
jungle.set_panda_power(True)
|
||||
time.sleep(2)
|
||||
connect_to_pandas()
|
||||
|
||||
def can_loopback(sender):
|
||||
receivers = list(filter((lambda p: (p != sender)), [jungle] + pandas))
|
||||
|
||||
for bus in range(4):
|
||||
obd = False
|
||||
if bus == 3:
|
||||
obd = True
|
||||
bus = 1
|
||||
|
||||
# Clear buses
|
||||
for receiver in receivers:
|
||||
receiver.set_obd(obd)
|
||||
receiver.can_clear(bus) # TX
|
||||
receiver.can_clear(0xFFFF) # RX
|
||||
|
||||
# Send a random string
|
||||
addr = 0x18DB33F1 if FOR_RELEASE_BUILDS else random.randint(1, 2000)
|
||||
string = b"test"+os.urandom(4)
|
||||
sender.set_obd(obd)
|
||||
time.sleep(0.2)
|
||||
sender.can_send(addr, string, bus)
|
||||
time.sleep(0.2)
|
||||
|
||||
# Check if all receivers have indeed received them in their receiving buffers
|
||||
for receiver in receivers:
|
||||
content = receiver.can_recv()
|
||||
|
||||
# Check amount of messages
|
||||
if len(content) != 1:
|
||||
raise Exception("Amount of received CAN messages (" + str(len(content)) + ") does not equal 1. Bus: " + str(bus) +" OBD: " + str(obd))
|
||||
|
||||
# Check content
|
||||
if content[0][0] != addr or content[0][2] != string:
|
||||
raise Exception("Received CAN message content or address does not match")
|
||||
|
||||
# Check bus
|
||||
if content[0][3] != bus:
|
||||
raise Exception("Received CAN message bus does not match")
|
||||
|
||||
#################################################################
|
||||
############################# TEST ##############################
|
||||
#################################################################
|
||||
|
||||
def test_loopback():
|
||||
# disable safety modes
|
||||
for panda in pandas:
|
||||
panda.set_safety_mode(Panda.SAFETY_ELM327 if FOR_RELEASE_BUILDS else Panda.SAFETY_ALLOUTPUT)
|
||||
|
||||
# perform loopback with jungle as a sender
|
||||
can_loopback(jungle)
|
||||
|
||||
# perform loopback with each possible panda as a sender
|
||||
for panda in pandas:
|
||||
can_loopback(panda)
|
||||
|
||||
# enable safety modes
|
||||
for panda in pandas:
|
||||
panda.set_safety_mode(Panda.SAFETY_SILENT)
|
||||
|
||||
#################################################################
|
||||
############################# MAIN ##############################
|
||||
#################################################################
|
||||
jungle = None
|
||||
pandas = [] # type: ignore
|
||||
panda_serials = []
|
||||
counter = 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Connect to jungle silently
|
||||
print_colored("Connecting to jungle", "blue")
|
||||
with open(os.devnull, "w") as devnull:
|
||||
with contextlib.redirect_stdout(devnull):
|
||||
jungle = PandaJungle()
|
||||
jungle.set_panda_power(True)
|
||||
jungle.set_ignition(False)
|
||||
|
||||
# Connect to <NUM_PANDAS_PER_TEST> new pandas before starting tests
|
||||
print_colored("Waiting for " + str(NUM_PANDAS_PER_TEST) + " pandas to be connected", "yellow")
|
||||
while True:
|
||||
connected_serials = Panda.list()
|
||||
if len(connected_serials) == NUM_PANDAS_PER_TEST:
|
||||
panda_serials = connected_serials
|
||||
break
|
||||
|
||||
start_with_orientation(PandaJungle.HARNESS_ORIENTATION_1)
|
||||
|
||||
# Set bus speeds
|
||||
for device in pandas + [jungle]:
|
||||
for bus in range(len(BUS_SPEEDS)):
|
||||
device.set_can_speed_kbps(bus, BUS_SPEEDS[bus])
|
||||
|
||||
# Run test
|
||||
while True:
|
||||
test_loopback()
|
||||
counter += 1
|
||||
print_colored(str(counter) + " loopback cycles complete", "blue")
|
||||
45
panda/board/jungle/scripts/panda_identification_test.py
Executable file
45
panda/board/jungle/scripts/panda_identification_test.py
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
import random
|
||||
import contextlib
|
||||
|
||||
from panda import PandaJungle
|
||||
from panda import Panda
|
||||
|
||||
PANDA_UNDER_TEST = Panda.HW_TYPE_UNO
|
||||
|
||||
panda_jungle = PandaJungle()
|
||||
|
||||
def silent_panda_connect():
|
||||
with open(os.devnull, "w") as devnull:
|
||||
with contextlib.redirect_stdout(devnull):
|
||||
panda = Panda()
|
||||
return panda
|
||||
|
||||
def reboot_panda(harness_orientation=PandaJungle.HARNESS_ORIENTATION_NONE, ignition=False):
|
||||
print(f"Restarting panda with harness orientation: {harness_orientation} and ignition: {ignition}")
|
||||
panda_jungle.set_panda_power(False)
|
||||
panda_jungle.set_harness_orientation(harness_orientation)
|
||||
panda_jungle.set_ignition(ignition)
|
||||
time.sleep(2)
|
||||
panda_jungle.set_panda_power(True)
|
||||
time.sleep(2)
|
||||
|
||||
count = 0
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
ignition = random.randint(0, 1)
|
||||
harness_orientation = random.randint(0, 2)
|
||||
reboot_panda(harness_orientation, ignition)
|
||||
|
||||
p = silent_panda_connect()
|
||||
assert p.get_type() == PANDA_UNDER_TEST
|
||||
assert p.health()['car_harness_status'] == harness_orientation
|
||||
if harness_orientation != PandaJungle.HARNESS_ORIENTATION_NONE:
|
||||
assert p.health()['ignition_line'] == ignition
|
||||
|
||||
count += 1
|
||||
print(f"Passed {count} loops")
|
||||
|
||||
|
||||
18
panda/board/jungle/scripts/start.py
Executable file
18
panda/board/jungle/scripts/start.py
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
from panda import PandaJungle
|
||||
|
||||
if __name__ == "__main__":
|
||||
jungle = PandaJungle()
|
||||
|
||||
# If first argument specified, it sets the ignition (0 or 1)
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] == '1':
|
||||
jungle.set_ignition(True)
|
||||
else:
|
||||
jungle.set_ignition(False)
|
||||
jungle.set_harness_orientation(1)
|
||||
jungle.set_panda_power(True)
|
||||
|
||||
|
||||
7
panda/board/jungle/stm32fx/board.h
Normal file
7
panda/board/jungle/stm32fx/board.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "boards/board_declarations.h"
|
||||
#include "boards/board_v1.h"
|
||||
|
||||
void detect_board_type(void) {
|
||||
hw_type = HW_TYPE_V1;
|
||||
current_board = &board_v1;
|
||||
}
|
||||
9
panda/board/jungle/stm32h7/board.h
Normal file
9
panda/board/jungle/stm32h7/board.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "boards/board_declarations.h"
|
||||
|
||||
#include "stm32h7/lladc.h"
|
||||
#include "boards/board_v2.h"
|
||||
|
||||
void detect_board_type(void) {
|
||||
hw_type = HW_TYPE_V2;
|
||||
current_board = &board_v2;
|
||||
}
|
||||
54
panda/board/jungle/stm32h7/lladc.h
Normal file
54
panda/board/jungle/stm32h7/lladc.h
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user