Skip to content

Commit d7120be

Browse files
panda jungle (commaai#1547)
* mv jungle * match pedal style * fix linter * fix fw path * fix paths * safety!
1 parent e7894e6 commit d7120be

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1755
-64
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ a.out
1212
.#*
1313
dist/
1414
pandacan.egg-info/
15-
board/obj/
15+
obj/
1616
examples/output.csv
1717
.DS_Store
1818
.vscode*

Dockerfile

-7
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,6 @@ RUN cd /tmp && \
8080
rm -rf /tmp/openpilot/panda && \
8181
rm -rf /tmp/tmppilot
8282

83-
RUN cd /tmp/openpilot && \
84-
git clone https://github.com/commaai/panda_jungle.git && \
85-
cd panda_jungle && \
86-
git fetch && \
87-
git checkout 3a791be1f1877a69cf45de16a670992380622297 && \
88-
rm -rf .git/
89-
9083
RUN cd /tmp/openpilot && \
9184
pip install --no-cache-dir -r opendbc/requirements.txt && \
9285
pip install --no-cache-dir --upgrade aenum lru-dict pycurl tenacity atomicwrites serial smbus2

SConscript

+3
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ SConscript('board/SConscript')
182182
# pedal fw
183183
SConscript('board/pedal/SConscript')
184184

185+
# panda jungle fw
186+
SConscript('board/jungle/SConscript')
187+
185188
# test files
186189
if GetOption('test'):
187190
SConscript('tests/libpanda/SConscript')

__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
from .python import (Panda, PandaDFU, # noqa: F401
66
pack_can_buffer, unpack_can_buffer, calculate_checksum, unpack_log,
77
DLC_TO_LEN, LEN_TO_DLC, ALTERNATIVE_EXPERIENCE, CANPACKET_HEAD_SIZE)
8+
9+
10+
# panda jungle
11+
from .board.jungle import PandaJungle, PandaJungleDFU # noqa: F401

board/bootstub.c

+4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ int main(void) {
4141
clock_init();
4242
detect_board_type();
4343

44+
#ifdef PANDA_JUNGLE
45+
current_board->set_panda_power(true);
46+
#endif
47+
4448
if (enter_bootloader_mode == ENTER_SOFTLOADER_MAGIC) {
4549
enter_bootloader_mode = 0;
4650
soft_flasher_start();

board/config.h

+11-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,18 @@
2121
// USB definitions
2222
#define USB_VID 0xBBAAU
2323

24-
#ifdef BOOTSTUB
25-
#define USB_PID 0xDDEEU
24+
#ifdef PANDA_JUNGLE
25+
#ifdef BOOTSTUB
26+
#define USB_PID 0xDDEFU
27+
#else
28+
#define USB_PID 0xDDCFU
29+
#endif
2630
#else
27-
#define USB_PID 0xDDCCU
31+
#ifdef BOOTSTUB
32+
#define USB_PID 0xDDEEU
33+
#else
34+
#define USB_PID 0xDDCCU
35+
#endif
2836
#endif
2937

3038
// platform includes

board/drivers/can_common.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ typedef struct {
88
typedef struct {
99
uint8_t bus_lookup;
1010
uint8_t can_num_lookup;
11+
int8_t forwarding_bus;
1112
uint32_t can_speed;
1213
uint32_t can_data_speed;
1314
bool canfd_enabled;
@@ -157,14 +158,15 @@ void can_clear(can_ring *q) {
157158
// can number: numeric lookup for MCU CAN interfaces (0 = CAN1, 1 = CAN2, etc);
158159
// bus_lookup: Translates from 'can number' to 'bus number'.
159160
// can_num_lookup: Translates from 'bus number' to 'can number'.
161+
// forwarding bus: If >= 0, forward all messages from this bus to the specified bus.
160162

161163
// Helpers
162164
// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3
163165
bus_config_t bus_config[] = {
164-
{ .bus_lookup = 0U, .can_num_lookup = 0U, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
165-
{ .bus_lookup = 1U, .can_num_lookup = 1U, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
166-
{ .bus_lookup = 2U, .can_num_lookup = 2U, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
167-
{ .bus_lookup = 0xFFU, .can_num_lookup = 0xFFU, .can_speed = 333U, .can_data_speed = 333U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
166+
{ .bus_lookup = 0U, .can_num_lookup = 0U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
167+
{ .bus_lookup = 1U, .can_num_lookup = 1U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
168+
{ .bus_lookup = 2U, .can_num_lookup = 2U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
169+
{ .bus_lookup = 0xFFU, .can_num_lookup = 0xFFU, .forwarding_bus = -1, .can_speed = 333U, .can_data_speed = 333U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
168170
};
169171

170172
#define CANIF_FROM_CAN_NUM(num) (cans[num])
@@ -190,6 +192,10 @@ void can_flip_buses(uint8_t bus1, uint8_t bus2){
190192
bus_config[bus2].can_num_lookup = bus1;
191193
}
192194

195+
void can_set_forwarding(uint8_t from, uint8_t to) {
196+
bus_config[from].forwarding_bus = to;
197+
}
198+
193199
void ignition_can_hook(CANPacket_t *to_push) {
194200
int bus = GET_BUS(to_push);
195201
int addr = GET_ADDR(to_push);

board/drivers/fdcan.h

+3
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ void can_rx(uint8_t can_number) {
195195

196196
// forwarding (panda only)
197197
int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr);
198+
if (bus_fwd_num < 0) {
199+
bus_fwd_num = bus_config[can_number].forwarding_bus;
200+
}
198201
if (bus_fwd_num != -1) {
199202
CANPacket_t to_send;
200203

board/drivers/gpio.h

+11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
#define OUTPUT_TYPE_PUSH_PULL 0U
1111
#define OUTPUT_TYPE_OPEN_DRAIN 1U
1212

13+
typedef struct {
14+
GPIO_TypeDef *bank;
15+
uint8_t pin;
16+
} gpio_t;
17+
1318
void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) {
1419
ENTER_CRITICAL();
1520
uint32_t tmp = GPIO->MODER;
@@ -63,6 +68,12 @@ int get_gpio_input(GPIO_TypeDef *GPIO, unsigned int pin) {
6368
return (GPIO->IDR & (1U << pin)) == (1U << pin);
6469
}
6570

71+
void gpio_set_all_output(const gpio_t *pins, uint8_t num_pins, bool enabled) {
72+
for (uint8_t i = 0; i < num_pins; i++) {
73+
set_gpio_output(pins[i].bank, pins[i].pin, enabled);
74+
}
75+
}
76+
6677
// Detection with internal pullup
6778
#define PULL_EFFECTIVE_DELAY 4096
6879
bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) {

board/jungle/README.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Welcome to the jungle
2+
======
3+
4+
Firmware for the Panda Jungle testing board.
5+
Available for purchase at the [comma shop](https://comma.ai/shop/panda-jungle).
6+
7+
## udev rules
8+
9+
To make the jungle usable without root permissions, you might need to setup udev rules for it.
10+
On ubuntu, this should do the trick:
11+
``` bash
12+
sudo tee /etc/udev/rules.d/12-panda_jungle.rules <<EOF
13+
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddcf", MODE="0666"
14+
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddef", MODE="0666"
15+
EOF
16+
sudo udevadm control --reload-rules && sudo udevadm trigger
17+
```
18+
19+
## updating the firmware
20+
Updating the firmware is easy! In the `board/jungle/` folder, run:
21+
``` bash
22+
./flash.py
23+
```
24+
25+
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.
26+
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.

board/jungle/SConscript

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
import copy
3+
4+
Import('build_project', 'base_project_f4', 'base_project_h7')
5+
6+
build_projects = {
7+
"panda_jungle": base_project_f4,
8+
"panda_jungle_h7": base_project_h7,
9+
}
10+
11+
for project_name, project in build_projects.items():
12+
flags = [
13+
"-DPANDA_JUNGLE",
14+
"-DPANDA_BUS_CNT=3",
15+
]
16+
if os.getenv("FINAL_PROVISIONING"):
17+
flags += ["-DFINAL_PROVISIONING"]
18+
19+
build_project(project_name, project, flags)

board/jungle/__init__.py

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# python library to interface with panda
2+
import os
3+
import struct
4+
from functools import wraps
5+
from typing import Optional
6+
7+
from panda import Panda, PandaDFU
8+
from panda.python.constants import McuType
9+
10+
BASEDIR = os.path.dirname(os.path.realpath(__file__))
11+
FW_PATH = os.path.join(BASEDIR, "obj/")
12+
13+
14+
def ensure_jungle_health_packet_version(fn):
15+
@wraps(fn)
16+
def wrapper(self, *args, **kwargs):
17+
if self.health_version != self.HEALTH_PACKET_VERSION:
18+
raise RuntimeError(f"Jungle firmware ({self.health_version}) doesn't match the \
19+
library's health packet version ({self.HEALTH_PACKET_VERSION}). \
20+
Reflash jungle.")
21+
return fn(self, *args, **kwargs)
22+
return wrapper
23+
24+
25+
class PandaJungleDFU(PandaDFU):
26+
def recover(self):
27+
fn = os.path.join(FW_PATH, self._mcu_type.config.bootstub_fn.replace("panda", "panda_jungle"))
28+
with open(fn, "rb") as f:
29+
code = f.read()
30+
self.program_bootstub(code)
31+
self.reset()
32+
33+
34+
class PandaJungle(Panda):
35+
USB_PIDS = (0xddef, 0xddcf)
36+
37+
HW_TYPE_UNKNOWN = b'\x00'
38+
HW_TYPE_V1 = b'\x01'
39+
HW_TYPE_V2 = b'\x02'
40+
41+
F4_DEVICES = [HW_TYPE_V1, ]
42+
H7_DEVICES = [HW_TYPE_V2, ]
43+
44+
HEALTH_PACKET_VERSION = 1
45+
HEALTH_STRUCT = struct.Struct("<IffffffHHHHHHHHHHHH")
46+
47+
HARNESS_ORIENTATION_NONE = 0
48+
HARNESS_ORIENTATION_1 = 1
49+
HARNESS_ORIENTATION_2 = 2
50+
51+
def flash(self, fn=None, code=None, reconnect=True):
52+
if not fn:
53+
fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn.replace("panda", "panda_jungle"))
54+
super().flash(fn=fn, code=code, reconnect=reconnect)
55+
56+
def recover(self, timeout: Optional[int] = 60, reset: bool = True) -> bool:
57+
dfu_serial = self.get_dfu_serial()
58+
59+
if reset:
60+
self.reset(enter_bootstub=True)
61+
self.reset(enter_bootloader=True)
62+
63+
if not self.wait_for_dfu(dfu_serial, timeout=timeout):
64+
return False
65+
66+
dfu = PandaJungleDFU(dfu_serial)
67+
dfu.recover()
68+
69+
# reflash after recover
70+
self.connect(True, True)
71+
self.flash()
72+
return True
73+
74+
def get_mcu_type(self) -> McuType:
75+
hw_type = self.get_type()
76+
if hw_type in PandaJungle.F4_DEVICES:
77+
return McuType.F4
78+
elif hw_type in PandaJungle.H7_DEVICES:
79+
return McuType.H7
80+
raise ValueError(f"unknown HW type: {hw_type}")
81+
82+
# ******************* health *******************
83+
84+
@ensure_jungle_health_packet_version
85+
def health(self):
86+
dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xd2, 0, 0, self.HEALTH_STRUCT.size)
87+
a = self.HEALTH_STRUCT.unpack(dat)
88+
return {
89+
"uptime": a[0],
90+
"ch1_power": a[1],
91+
"ch2_power": a[2],
92+
"ch3_power": a[3],
93+
"ch4_power": a[4],
94+
"ch5_power": a[5],
95+
"ch6_power": a[6],
96+
"ch1_sbu1_voltage": a[7] / 1000.0,
97+
"ch1_sbu2_voltage": a[8] / 1000.0,
98+
"ch2_sbu1_voltage": a[9] / 1000.0,
99+
"ch2_sbu2_voltage": a[10] / 1000.0,
100+
"ch3_sbu1_voltage": a[11] / 1000.0,
101+
"ch3_sbu2_voltage": a[12] / 1000.0,
102+
"ch4_sbu1_voltage": a[13] / 1000.0,
103+
"ch4_sbu2_voltage": a[14] / 1000.0,
104+
"ch5_sbu1_voltage": a[15] / 1000.0,
105+
"ch5_sbu2_voltage": a[16] / 1000.0,
106+
"ch6_sbu1_voltage": a[17] / 1000.0,
107+
"ch6_sbu2_voltage": a[18] / 1000.0,
108+
}
109+
110+
# ******************* control *******************
111+
112+
# Returns tuple with health packet version and CAN packet/USB packet version
113+
def get_packets_versions(self):
114+
dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xdd, 0, 0, 3)
115+
if dat and len(dat) == 3:
116+
a = struct.unpack("BBB", dat)
117+
return (a[0], a[1], a[2])
118+
return (-1, -1, -1)
119+
120+
# ******************* jungle stuff *******************
121+
122+
def set_panda_power(self, enabled):
123+
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa0, int(enabled), 0, b'')
124+
125+
def set_harness_orientation(self, mode):
126+
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa1, int(mode), 0, b'')
127+
128+
def set_ignition(self, enabled):
129+
self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa2, int(enabled), 0, b'')
130+
131+
# ******************* serial *******************
132+
133+
def debug_read(self):
134+
ret = []
135+
while 1:
136+
lret = bytes(self._handle.controlRead(PandaJungle.REQUEST_IN, 0xe0, 0, 0, 0x40))
137+
if len(lret) == 0:
138+
break
139+
ret.append(lret)
140+
return b''.join(ret)
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// ******************** Prototypes ********************
2+
typedef void (*board_init)(void);
3+
typedef void (*board_set_led)(uint8_t color, bool enabled);
4+
typedef void (*board_board_tick)(void);
5+
typedef bool (*board_get_button)(void);
6+
typedef void (*board_set_panda_power)(bool enabled);
7+
typedef void (*board_set_ignition)(bool enabled);
8+
typedef void (*board_set_harness_orientation)(uint8_t orientation);
9+
typedef void (*board_set_can_mode)(uint8_t mode);
10+
typedef void (*board_enable_can_transciever)(uint8_t transciever, bool enabled);
11+
typedef float (*board_get_channel_power)(uint8_t channel);
12+
typedef uint16_t (*board_get_sbu_mV)(uint8_t channel, uint8_t sbu);
13+
14+
struct board {
15+
const char *board_type;
16+
const bool has_canfd;
17+
const uint16_t avdd_mV;
18+
board_init init;
19+
board_set_led set_led;
20+
board_board_tick board_tick;
21+
board_get_button get_button;
22+
board_set_panda_power set_panda_power;
23+
board_set_ignition set_ignition;
24+
board_set_harness_orientation set_harness_orientation;
25+
board_set_can_mode set_can_mode;
26+
board_enable_can_transciever enable_can_transciever;
27+
board_get_channel_power get_channel_power;
28+
board_get_sbu_mV get_sbu_mV;
29+
30+
// TODO: shouldn't need these
31+
bool has_spi;
32+
bool has_hw_gmlan;
33+
};
34+
35+
// ******************* Definitions ********************
36+
#define HW_TYPE_UNKNOWN 0U
37+
#define HW_TYPE_V1 1U
38+
#define HW_TYPE_V2 2U
39+
40+
// LED colors
41+
#define LED_RED 0U
42+
#define LED_GREEN 1U
43+
#define LED_BLUE 2U
44+
45+
// CAN modes
46+
#define CAN_MODE_NORMAL 0U
47+
#define CAN_MODE_GMLAN_CAN2 1U
48+
#define CAN_MODE_GMLAN_CAN3 2U
49+
#define CAN_MODE_OBD_CAN2 3U
50+
51+
// Harness states
52+
#define HARNESS_ORIENTATION_NONE 0U
53+
#define HARNESS_ORIENTATION_1 1U
54+
#define HARNESS_ORIENTATION_2 2U
55+
56+
#define SBU1 0U
57+
#define SBU2 1U
58+
59+
// ********************* Globals **********************
60+
uint8_t harness_orientation = HARNESS_ORIENTATION_NONE;
61+
uint8_t can_mode = CAN_MODE_NORMAL;
62+
bool ignition = false;

0 commit comments

Comments
 (0)