mirror of
https://github.com/infiniteCable2/panda.git
synced 2026-06-08 10:54:55 +08:00
Merge branch 'master' of https://github.com/sunnypilot/panda
This commit is contained in:
30
.github/workflows/test.yaml
vendored
30
.github/workflows/test.yaml
vendored
@@ -44,18 +44,28 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- run: ./test.sh
|
||||
|
||||
windows:
|
||||
name: windows pip package test
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- uses: astral-sh/setup-uv@v5
|
||||
- name: Install package with uv
|
||||
run: uv pip install --system .
|
||||
- name: Verify importing panda
|
||||
run: python -c "from panda import Panda"
|
||||
|
||||
|
||||
misra_linter:
|
||||
name: MISRA C:2012 Linter
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache cppcheck
|
||||
id: cppcheck-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: tests/misra/cppcheck
|
||||
key: cppcheck-${{ hashFiles('tests/misra/*') }}
|
||||
- run: ./setup.sh
|
||||
- name: Build FW
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
@@ -68,16 +78,8 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache cppcheck
|
||||
id: cppcheck-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: tests/misra/cppcheck
|
||||
key: cppcheck-${{ hashFiles('tests/misra/*') }}
|
||||
- run: ./setup.sh
|
||||
- name: Build FW
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: tests/misra/install.sh
|
||||
run: ${{ env.RUN }} "cd tests/misra && ./install.sh"
|
||||
- name: MISRA mutation tests
|
||||
run: ${{ env.RUN }} "cd tests/misra && pytest test_mutation.py"
|
||||
|
||||
41
.github/workflows/update-cppcheck.yml
vendored
41
.github/workflows/update-cppcheck.yml
vendored
@@ -1,41 +0,0 @@
|
||||
name: Update cppcheck
|
||||
|
||||
on:
|
||||
#push:
|
||||
schedule:
|
||||
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-cppcheck:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get latest cppcheck version
|
||||
id: version
|
||||
run: |
|
||||
# Tags are sorted by time (newest first), so get the first version-like tag
|
||||
LATEST=$(curl -fsSL "https://api.github.com/repos/danmar/cppcheck/tags?per_page=20" | \
|
||||
jq -r '.[].name' | \
|
||||
grep -E '^[0-9]+\.[0-9]+(\.[0-9]+)?$' | \
|
||||
head -n 1)
|
||||
echo "vers=$LATEST" >> "$GITHUB_OUTPUT"
|
||||
- name: Update VERS in install.sh
|
||||
run: |
|
||||
sed -i "s/^VERS=\".*\"/VERS=\"${{ steps.version.outputs.vers }}\"/" tests/misra/install.sh
|
||||
grep VERS tests/misra/install.sh
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||
with:
|
||||
author: Vehicle Researcher <user@comma.ai>
|
||||
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
|
||||
commit-message: "[bot] Update cppcheck to ${{ steps.version.outputs.vers }}"
|
||||
title: "[bot] Update cppcheck to ${{ steps.version.outputs.vers }}"
|
||||
body: "See all cppcheck releases: https://github.com/danmar/cppcheck/releases"
|
||||
branch: "update-cppcheck"
|
||||
base: "master"
|
||||
delete-branch: true
|
||||
labels: bot
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -37,3 +37,6 @@ tests/safety/coverage.info
|
||||
*.profraw
|
||||
*.profdata
|
||||
mull.yml
|
||||
|
||||
.claude/
|
||||
TASK.md
|
||||
|
||||
@@ -8,7 +8,8 @@ WORKDIR $WORKDIR
|
||||
|
||||
# deps install
|
||||
COPY pyproject.toml __init__.py setup.sh $WORKDIR
|
||||
RUN mkdir -p $WORKDIR/python/ && touch $WORKDIR/__init__.py
|
||||
RUN mkdir -p $WORKDIR/python/ $WORKDIR/board/body/ $WORKDIR/board/jungle/ && \
|
||||
touch $WORKDIR/__init__.py $WORKDIR/board/__init__.py $WORKDIR/board/body/__init__.py $WORKDIR/board/jungle/__init__.py
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends sudo && DEBIAN_FRONTEND=noninteractive $WORKDIR/setup.sh
|
||||
|
||||
# second pass for the opendbc moving tag
|
||||
|
||||
8
Jenkinsfile
vendored
8
Jenkinsfile
vendored
@@ -15,7 +15,7 @@ def docker_run(String step_label, int timeout_mins, String cmd) {
|
||||
def phone(String ip, String step_label, String cmd) {
|
||||
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
|
||||
def ssh_cmd = """
|
||||
ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
|
||||
ssh -tt -o StrictHostKeyChecking=no -o ConnectTimeout=30 -o ConnectionAttempts=3 -o ServerAliveInterval=10 -o ServerAliveCountMax=3 -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
|
||||
|
||||
set -e
|
||||
|
||||
@@ -51,7 +51,11 @@ END"""
|
||||
def phone_steps(String device_type, steps) {
|
||||
lock(resource: "", label: device_type, inversePrecedence: true, variable: 'device_ip', quantity: 1) {
|
||||
timeout(time: 20, unit: 'MINUTES') {
|
||||
phone(device_ip, "git checkout", readFile("tests/setup_device_ci.sh"),)
|
||||
retry (3) {
|
||||
def date = sh(script: 'date', returnStdout: true).trim()
|
||||
phone(device_ip, "set time", "date -s '${date}'")
|
||||
phone(device_ip, "git checkout", readFile("tests/setup_device_ci.sh"))
|
||||
}
|
||||
steps.each { item ->
|
||||
phone(device_ip, item[0], item[1])
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ static void cuatro_set_fan_enabled(bool enabled) {
|
||||
|
||||
static void cuatro_set_bootkick(BootState state) {
|
||||
set_gpio_output(GPIOA, 0, state != BOOT_BOOTKICK);
|
||||
// DC_IN rising edge wakes SOM from ship mode
|
||||
set_gpio_output(GPIOC, 11, state != BOOT_BOOTKICK);
|
||||
}
|
||||
|
||||
static void cuatro_set_amp_enabled(bool enabled) {
|
||||
@@ -50,7 +52,7 @@ static void cuatro_init(void) {
|
||||
|
||||
// open drain
|
||||
set_gpio_output_type(GPIOD, 3, OUTPUT_TYPE_OPEN_DRAIN); // FAN_EN
|
||||
set_gpio_output_type(GPIOC, 12, OUTPUT_TYPE_OPEN_DRAIN); // VBAT_EN
|
||||
set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); // DC_IN_EN_N
|
||||
|
||||
// Power readout
|
||||
set_gpio_mode(GPIOC, 5, MODE_ANALOG);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "board/can.h"
|
||||
#include "board/health.h"
|
||||
#include "board/body/motor_control.h"
|
||||
#include "board/drivers/can_common_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
#include "opendbc/safety/declarations.h"
|
||||
|
||||
#define BODY_CAN_ADDR_MOTOR_SPEED 0x201U
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
// ******************** Prototypes ********************
|
||||
void print(const char *a){ UNUSED(a); }
|
||||
void puth(uint8_t i){ UNUSED(i); }
|
||||
void puth2(uint8_t i){ UNUSED(i); }
|
||||
void puth4(uint8_t i){ UNUSED(i); }
|
||||
void puth(unsigned int i){ UNUSED(i); }
|
||||
void puth2(unsigned int i){ UNUSED(i); }
|
||||
__attribute__((unused)) static void puth4(unsigned int i){ UNUSED(i); }
|
||||
void hexdump(const void *a, int l){ UNUSED(a); UNUSED(l); }
|
||||
typedef struct board board;
|
||||
typedef struct harness_configuration harness_configuration;
|
||||
void pwm_init(TIM_TypeDef *TIM, uint8_t channel);
|
||||
void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage);
|
||||
// No UART support in bootloader
|
||||
typedef struct uart_ring {} uart_ring;
|
||||
uart_ring uart_ring_som_debug;
|
||||
typedef struct uart_ring uart_ring;
|
||||
void uart_init(uart_ring *q, int baud) { UNUSED(q); UNUSED(baud); }
|
||||
|
||||
// ********************* Globals **********************
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// ********************* Critical section helpers *********************
|
||||
void enable_interrupts(void);
|
||||
void disable_interrupts(void);
|
||||
|
||||
extern uint8_t global_critical_depth;
|
||||
|
||||
#define ENTER_CRITICAL() \
|
||||
__disable_irq(); \
|
||||
global_critical_depth += 1U;
|
||||
|
||||
#define EXIT_CRITICAL() \
|
||||
global_critical_depth -= 1U; \
|
||||
if ((global_critical_depth == 0U) && interrupts_enabled) { \
|
||||
__enable_irq(); \
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "bootkick_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
bool bootkick_reset_triggered = false;
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
extern bool bootkick_reset_triggered;
|
||||
|
||||
void bootkick_tick(bool ignition, bool recent_heartbeat);
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "can_common_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
uint32_t safety_tx_blocked = 0;
|
||||
uint32_t safety_rx_invalid = 0;
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/can.h"
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t w_ptr;
|
||||
volatile uint32_t r_ptr;
|
||||
uint32_t fifo_size;
|
||||
CANPacket_t *elems;
|
||||
} can_ring;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bus_lookup;
|
||||
uint8_t can_num_lookup;
|
||||
int8_t forwarding_bus;
|
||||
uint32_t can_speed;
|
||||
uint32_t can_data_speed;
|
||||
bool canfd_auto;
|
||||
bool canfd_enabled;
|
||||
bool brs_enabled;
|
||||
bool canfd_non_iso;
|
||||
} bus_config_t;
|
||||
|
||||
extern uint32_t safety_tx_blocked;
|
||||
extern uint32_t safety_rx_invalid;
|
||||
extern uint32_t tx_buffer_overflow;
|
||||
extern uint32_t rx_buffer_overflow;
|
||||
|
||||
extern can_health_t can_health[PANDA_CAN_CNT];
|
||||
|
||||
// Ignition detected from CAN meessages
|
||||
extern bool ignition_can;
|
||||
extern uint32_t ignition_can_cnt;
|
||||
|
||||
extern bool can_silent;
|
||||
extern bool can_loopback;
|
||||
|
||||
// ******************* functions prototypes *********************
|
||||
bool can_init(uint8_t can_number);
|
||||
void process_can(uint8_t can_number);
|
||||
|
||||
// ********************* instantiate queues *********************
|
||||
extern can_ring *can_queues[PANDA_CAN_CNT];
|
||||
|
||||
// helpers
|
||||
#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFFU); 1[dst8] = (((src32) >> 8U) & 0xFFU); 2[dst8] = (((src32) >> 16U) & 0xFFU); 3[dst8] = (((src32) >> 24U) & 0xFFU)
|
||||
#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8U) | (2[src8] << 16U) | (3[src8] << 24U))
|
||||
|
||||
// ********************* interrupt safe queue *********************
|
||||
bool can_pop(can_ring *q, CANPacket_t *elem);
|
||||
bool can_push(can_ring *q, const CANPacket_t *elem);
|
||||
uint32_t can_slots_empty(const can_ring *q);
|
||||
extern bus_config_t bus_config[PANDA_CAN_CNT];
|
||||
|
||||
#define CANIF_FROM_CAN_NUM(num) (cans[num])
|
||||
#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup)
|
||||
#define CAN_NUM_FROM_BUS_NUM(num) (bus_config[num].can_num_lookup)
|
||||
|
||||
void can_init_all(void);
|
||||
void can_set_orientation(bool flipped);
|
||||
#ifdef PANDA_JUNGLE
|
||||
void can_set_forwarding(uint8_t from, uint8_t to);
|
||||
#endif
|
||||
void ignition_can_hook(CANPacket_t *to_push);
|
||||
bool can_tx_check_min_slots_free(uint32_t min);
|
||||
uint8_t calculate_checksum(const uint8_t *dat, uint32_t len);
|
||||
void can_set_checksum(CANPacket_t *packet);
|
||||
bool can_check_checksum(CANPacket_t *packet);
|
||||
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
|
||||
bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len);
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "clock_source_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
#define CLOCK_SOURCE_PERIOD_MS 50U
|
||||
#define CLOCK_SOURCE_PULSE_LEN_MS 2U
|
||||
|
||||
void clock_source_set_timer_params(uint16_t param1, uint16_t param2) {
|
||||
// Pulse length of each channel
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define CLOCK_SOURCE_PERIOD_MS 50U
|
||||
#define CLOCK_SOURCE_PULSE_LEN_MS 2U
|
||||
|
||||
void clock_source_set_timer_params(uint16_t param1, uint16_t param2);
|
||||
void clock_source_init(bool enable_channel1);
|
||||
287
board/drivers/drivers.h
Normal file
287
board/drivers/drivers.h
Normal file
@@ -0,0 +1,287 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/can.h"
|
||||
#include "board/health.h"
|
||||
#include "board/crc.h"
|
||||
#ifdef STM32H7
|
||||
#include "board/stm32h7/lladc_declarations.h"
|
||||
#endif
|
||||
|
||||
// ******************** bootkick ********************
|
||||
|
||||
extern bool bootkick_reset_triggered;
|
||||
|
||||
void bootkick_tick(bool ignition, bool recent_heartbeat);
|
||||
|
||||
// ******************** can_common ********************
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t w_ptr;
|
||||
volatile uint32_t r_ptr;
|
||||
uint32_t fifo_size;
|
||||
CANPacket_t *elems;
|
||||
} can_ring;
|
||||
|
||||
typedef struct {
|
||||
uint8_t bus_lookup;
|
||||
uint8_t can_num_lookup;
|
||||
int8_t forwarding_bus;
|
||||
uint32_t can_speed;
|
||||
uint32_t can_data_speed;
|
||||
bool canfd_auto;
|
||||
bool canfd_enabled;
|
||||
bool brs_enabled;
|
||||
bool canfd_non_iso;
|
||||
} bus_config_t;
|
||||
|
||||
extern uint32_t safety_tx_blocked;
|
||||
extern uint32_t safety_rx_invalid;
|
||||
extern uint32_t tx_buffer_overflow;
|
||||
extern uint32_t rx_buffer_overflow;
|
||||
|
||||
extern can_health_t can_health[PANDA_CAN_CNT];
|
||||
|
||||
// Ignition detected from CAN meessages
|
||||
extern bool ignition_can;
|
||||
extern uint32_t ignition_can_cnt;
|
||||
|
||||
extern bool can_silent;
|
||||
extern bool can_loopback;
|
||||
|
||||
// ******************* functions prototypes *********************
|
||||
bool can_init(uint8_t can_number);
|
||||
void process_can(uint8_t can_number);
|
||||
|
||||
// ********************* instantiate queues *********************
|
||||
extern can_ring *can_queues[PANDA_CAN_CNT];
|
||||
|
||||
// helpers
|
||||
#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFFU); 1[dst8] = (((src32) >> 8U) & 0xFFU); 2[dst8] = (((src32) >> 16U) & 0xFFU); 3[dst8] = (((src32) >> 24U) & 0xFFU)
|
||||
#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8U) | (2[src8] << 16U) | (3[src8] << 24U))
|
||||
|
||||
// ********************* interrupt safe queue *********************
|
||||
bool can_pop(can_ring *q, CANPacket_t *elem);
|
||||
bool can_push(can_ring *q, const CANPacket_t *elem);
|
||||
uint32_t can_slots_empty(const can_ring *q);
|
||||
extern bus_config_t bus_config[PANDA_CAN_CNT];
|
||||
|
||||
#define CANIF_FROM_CAN_NUM(num) (cans[num])
|
||||
#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup)
|
||||
#define CAN_NUM_FROM_BUS_NUM(num) (bus_config[num].can_num_lookup)
|
||||
|
||||
void can_init_all(void);
|
||||
void can_set_orientation(bool flipped);
|
||||
#ifdef PANDA_JUNGLE
|
||||
void can_set_forwarding(uint8_t from, uint8_t to);
|
||||
#endif
|
||||
void ignition_can_hook(CANPacket_t *to_push);
|
||||
bool can_tx_check_min_slots_free(uint32_t min);
|
||||
uint8_t calculate_checksum(const uint8_t *dat, uint32_t len);
|
||||
void can_set_checksum(CANPacket_t *packet);
|
||||
bool can_check_checksum(CANPacket_t *packet);
|
||||
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
|
||||
bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len);
|
||||
|
||||
// ******************** clock_source ********************
|
||||
|
||||
void clock_source_set_timer_params(uint16_t param1, uint16_t param2);
|
||||
void clock_source_init(bool enable_channel1);
|
||||
|
||||
// ******************** fan ********************
|
||||
|
||||
struct fan_state_t {
|
||||
uint16_t tach_counter;
|
||||
uint16_t rpm;
|
||||
uint8_t power;
|
||||
float error_integral;
|
||||
uint8_t cooldown_counter;
|
||||
};
|
||||
extern struct fan_state_t fan_state;
|
||||
|
||||
void fan_set_power(uint8_t percentage);
|
||||
void llfan_init(void);
|
||||
void fan_init(void);
|
||||
// Call this at FAN_TICK_FREQ
|
||||
void fan_tick(void);
|
||||
|
||||
// ******************** fdcan ********************
|
||||
#ifdef STM32H7
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t header[2];
|
||||
volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U];
|
||||
} canfd_fifo;
|
||||
|
||||
extern FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT];
|
||||
|
||||
#define CAN_ACK_ERROR 3U
|
||||
|
||||
void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number);
|
||||
void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg);
|
||||
|
||||
void process_can(uint8_t can_number);
|
||||
void can_rx(uint8_t can_number);
|
||||
bool can_init(uint8_t can_number);
|
||||
|
||||
// ******************** harness ********************
|
||||
|
||||
#define HARNESS_STATUS_NC 0U
|
||||
#define HARNESS_STATUS_NORMAL 1U
|
||||
#define HARNESS_STATUS_FLIPPED 2U
|
||||
|
||||
struct harness_t {
|
||||
uint8_t status;
|
||||
uint16_t sbu1_voltage_mV;
|
||||
uint16_t sbu2_voltage_mV;
|
||||
bool relay_driven;
|
||||
bool sbu_adc_lock;
|
||||
};
|
||||
extern struct harness_t harness;
|
||||
|
||||
struct harness_configuration {
|
||||
GPIO_TypeDef * const GPIO_SBU1;
|
||||
GPIO_TypeDef * const GPIO_SBU2;
|
||||
GPIO_TypeDef * const GPIO_relay_SBU1;
|
||||
GPIO_TypeDef * const GPIO_relay_SBU2;
|
||||
const uint8_t pin_SBU1;
|
||||
const uint8_t pin_SBU2;
|
||||
const uint8_t pin_relay_SBU1;
|
||||
const uint8_t pin_relay_SBU2;
|
||||
const adc_signal_t adc_signal_SBU1;
|
||||
const adc_signal_t adc_signal_SBU2;
|
||||
};
|
||||
|
||||
// The ignition relay is only used for testing purposes
|
||||
void set_intercept_relay(bool intercept, bool ignition_relay);
|
||||
bool harness_check_ignition(void);
|
||||
void harness_tick(void);
|
||||
void harness_init(void);
|
||||
|
||||
// ******************** interrupts ********************
|
||||
|
||||
typedef struct interrupt {
|
||||
IRQn_Type irq_type;
|
||||
void (*handler)(void);
|
||||
uint32_t call_counter;
|
||||
uint32_t call_rate;
|
||||
uint32_t max_call_rate; // Call rate is defined as the amount of calls each second
|
||||
uint32_t call_rate_fault;
|
||||
} interrupt;
|
||||
|
||||
void interrupt_timer_init(void);
|
||||
uint32_t microsecond_timer_get(void);
|
||||
void unused_interrupt_handler(void);
|
||||
|
||||
extern interrupt interrupts[NUM_INTERRUPTS];
|
||||
|
||||
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \
|
||||
interrupts[irq_num].irq_type = (irq_num); \
|
||||
interrupts[irq_num].handler = (func_ptr); \
|
||||
interrupts[irq_num].call_counter = 0U; \
|
||||
interrupts[irq_num].call_rate = 0U; \
|
||||
interrupts[irq_num].max_call_rate = (call_rate_max); \
|
||||
interrupts[irq_num].call_rate_fault = (rate_fault);
|
||||
|
||||
extern float interrupt_load;
|
||||
|
||||
void handle_interrupt(IRQn_Type irq_type);
|
||||
// Every second
|
||||
void interrupt_timer_handler(void);
|
||||
void init_interrupts(bool check_rate_limit);
|
||||
|
||||
#endif // STM32H7
|
||||
|
||||
// ******************** registers ********************
|
||||
|
||||
// 10 bit hash with 23 as a prime
|
||||
#define REGISTER_MAP_SIZE 0x3FFU
|
||||
#define HASHING_PRIME 23U
|
||||
|
||||
// Do not put bits in the check mask that get changed by the hardware
|
||||
void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask);
|
||||
// Set individual bits. Also add them to the check_mask.
|
||||
// Do not use this to change bits that get reset by the hardware
|
||||
void register_set_bits(volatile uint32_t *addr, uint32_t val);
|
||||
// Clear individual bits. Also add them to the check_mask.
|
||||
// Do not use this to clear bits that get set by the hardware
|
||||
void register_clear_bits(volatile uint32_t *addr, uint32_t val);
|
||||
// To be called periodically
|
||||
void check_registers(void);
|
||||
void init_registers(void);
|
||||
|
||||
// ******************** simple_watchdog ********************
|
||||
|
||||
typedef struct simple_watchdog_state_t {
|
||||
uint32_t fault;
|
||||
uint32_t last_ts;
|
||||
uint32_t threshold;
|
||||
} simple_watchdog_state_t;
|
||||
|
||||
void simple_watchdog_kick(void);
|
||||
void simple_watchdog_init(uint32_t fault, uint32_t threshold);
|
||||
|
||||
// ******************** spi ********************
|
||||
|
||||
// got max rate from hitting a non-existent endpoint
|
||||
// in a tight loop, plus some buffer
|
||||
#define SPI_IRQ_RATE 16000U
|
||||
|
||||
#define SPI_BUF_SIZE 4096U
|
||||
extern uint8_t spi_buf_rx[SPI_BUF_SIZE];
|
||||
extern uint8_t spi_buf_tx[SPI_BUF_SIZE];
|
||||
|
||||
extern uint16_t spi_error_count;
|
||||
|
||||
void can_tx_comms_resume_spi(void);
|
||||
void spi_init(void);
|
||||
void spi_rx_done(void);
|
||||
void spi_tx_done(bool reset);
|
||||
|
||||
// ******************** uart ********************
|
||||
#ifdef STM32H7
|
||||
|
||||
// ***************************** Definitions *****************************
|
||||
#define FIFO_SIZE_INT 0x400U
|
||||
|
||||
typedef struct uart_ring {
|
||||
volatile uint16_t w_ptr_tx;
|
||||
volatile uint16_t r_ptr_tx;
|
||||
uint8_t *elems_tx;
|
||||
uint32_t tx_fifo_size;
|
||||
volatile uint16_t w_ptr_rx;
|
||||
volatile uint16_t r_ptr_rx;
|
||||
uint8_t *elems_rx;
|
||||
uint32_t rx_fifo_size;
|
||||
USART_TypeDef *uart;
|
||||
void (*callback)(struct uart_ring*);
|
||||
bool overwrite;
|
||||
} uart_ring;
|
||||
|
||||
// ***************************** Function prototypes *****************************
|
||||
void debug_ring_callback(uart_ring *ring);
|
||||
void uart_tx_ring(uart_ring *q);
|
||||
uart_ring *get_ring_by_number(int a);
|
||||
// ************************* Low-level buffer functions *************************
|
||||
bool get_char(uart_ring *q, char *elem);
|
||||
bool injectc(uart_ring *q, char elem);
|
||||
bool put_char(uart_ring *q, char elem);
|
||||
void clear_uart_buff(uart_ring *q);
|
||||
// ************************ High-level debug functions **********************
|
||||
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(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
|
||||
|
||||
#endif // STM32H7
|
||||
|
||||
// ******************** usb ********************
|
||||
|
||||
void usb_init(void);
|
||||
void refresh_can_tx_slots_available(void);
|
||||
void can_tx_comms_resume_usb(void);
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "fan_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
struct fan_state_t fan_state;
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
struct fan_state_t {
|
||||
uint16_t tach_counter;
|
||||
uint16_t rpm;
|
||||
uint8_t power;
|
||||
float error_integral;
|
||||
uint8_t cooldown_counter;
|
||||
};
|
||||
extern struct fan_state_t fan_state;
|
||||
|
||||
void fan_set_power(uint8_t percentage);
|
||||
void llfan_init(void);
|
||||
void fan_init(void);
|
||||
// Call this at FAN_TICK_FREQ
|
||||
void fan_tick(void);
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "fdcan_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3};
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/can.h"
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t header[2];
|
||||
volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U];
|
||||
} canfd_fifo;
|
||||
|
||||
extern FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT];
|
||||
|
||||
#define CAN_ACK_ERROR 3U
|
||||
|
||||
void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number);
|
||||
void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg);
|
||||
|
||||
void process_can(uint8_t can_number);
|
||||
void can_rx(uint8_t can_number);
|
||||
bool can_init(uint8_t can_number);
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "harness_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
struct harness_t harness;
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define HARNESS_STATUS_NC 0U
|
||||
#define HARNESS_STATUS_NORMAL 1U
|
||||
#define HARNESS_STATUS_FLIPPED 2U
|
||||
|
||||
struct harness_t {
|
||||
uint8_t status;
|
||||
uint16_t sbu1_voltage_mV;
|
||||
uint16_t sbu2_voltage_mV;
|
||||
bool relay_driven;
|
||||
bool sbu_adc_lock;
|
||||
};
|
||||
extern struct harness_t harness;
|
||||
|
||||
struct harness_configuration {
|
||||
GPIO_TypeDef * const GPIO_SBU1;
|
||||
GPIO_TypeDef * const GPIO_SBU2;
|
||||
GPIO_TypeDef * const GPIO_relay_SBU1;
|
||||
GPIO_TypeDef * const GPIO_relay_SBU2;
|
||||
const uint8_t pin_SBU1;
|
||||
const uint8_t pin_SBU2;
|
||||
const uint8_t pin_relay_SBU1;
|
||||
const uint8_t pin_relay_SBU2;
|
||||
const adc_signal_t adc_signal_SBU1;
|
||||
const adc_signal_t adc_signal_SBU2;
|
||||
};
|
||||
|
||||
// The ignition relay is only used for testing purposes
|
||||
void set_intercept_relay(bool intercept, bool ignition_relay);
|
||||
bool harness_check_ignition(void);
|
||||
void harness_tick(void);
|
||||
void harness_init(void);
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "interrupts_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
void unused_interrupt_handler(void) {
|
||||
// Something is wrong if this handler is called!
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct interrupt {
|
||||
IRQn_Type irq_type;
|
||||
void (*handler)(void);
|
||||
uint32_t call_counter;
|
||||
uint32_t call_rate;
|
||||
uint32_t max_call_rate; // Call rate is defined as the amount of calls each second
|
||||
uint32_t call_rate_fault;
|
||||
} interrupt;
|
||||
|
||||
void interrupt_timer_init(void);
|
||||
uint32_t microsecond_timer_get(void);
|
||||
void unused_interrupt_handler(void);
|
||||
|
||||
extern interrupt interrupts[NUM_INTERRUPTS];
|
||||
|
||||
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \
|
||||
interrupts[irq_num].irq_type = (irq_num); \
|
||||
interrupts[irq_num].handler = (func_ptr); \
|
||||
interrupts[irq_num].call_counter = 0U; \
|
||||
interrupts[irq_num].call_rate = 0U; \
|
||||
interrupts[irq_num].max_call_rate = (call_rate_max); \
|
||||
interrupts[irq_num].call_rate_fault = (rate_fault);
|
||||
|
||||
extern float interrupt_load;
|
||||
|
||||
void handle_interrupt(IRQn_Type irq_type);
|
||||
// Every second
|
||||
void interrupt_timer_handler(void);
|
||||
void init_interrupts(bool check_rate_limit);
|
||||
@@ -1,4 +1,13 @@
|
||||
#include "registers_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
typedef struct reg {
|
||||
volatile uint32_t *address;
|
||||
uint32_t value;
|
||||
uint32_t check_mask;
|
||||
bool logged_fault;
|
||||
} reg;
|
||||
|
||||
#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr)))
|
||||
|
||||
static reg register_map[REGISTER_MAP_SIZE];
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct reg {
|
||||
volatile uint32_t *address;
|
||||
uint32_t value;
|
||||
uint32_t check_mask;
|
||||
} reg;
|
||||
|
||||
// 10 bit hash with 23 as a prime
|
||||
#define REGISTER_MAP_SIZE 0x3FFU
|
||||
#define HASHING_PRIME 23U
|
||||
#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr)))
|
||||
|
||||
// Do not put bits in the check mask that get changed by the hardware
|
||||
void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask);
|
||||
// Set individual bits. Also add them to the check_mask.
|
||||
// Do not use this to change bits that get reset by the hardware
|
||||
void register_set_bits(volatile uint32_t *addr, uint32_t val);
|
||||
// Clear individual bits. Also add them to the check_mask.
|
||||
// Do not use this to clear bits that get set by the hardware
|
||||
void register_clear_bits(volatile uint32_t *addr, uint32_t val);
|
||||
// To be called periodically
|
||||
void check_registers(void);
|
||||
void init_registers(void);
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "simple_watchdog_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
static simple_watchdog_state_t wd_state;
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct simple_watchdog_state_t {
|
||||
uint32_t fault;
|
||||
uint32_t last_ts;
|
||||
uint32_t threshold;
|
||||
} simple_watchdog_state_t;
|
||||
|
||||
void simple_watchdog_kick(void);
|
||||
void simple_watchdog_init(uint32_t fault, uint32_t threshold);
|
||||
@@ -1,13 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/drivers/spi_declarations.h"
|
||||
#include "board/crc.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2
|
||||
#ifdef STM32H7
|
||||
__attribute__((section(".sram12"))) uint8_t spi_buf_rx[SPI_BUF_SIZE];
|
||||
__attribute__((section(".sram12"))) uint8_t spi_buf_tx[SPI_BUF_SIZE];
|
||||
#else
|
||||
uint8_t spi_buf_rx[SPI_BUF_SIZE];
|
||||
uint8_t spi_buf_tx[SPI_BUF_SIZE];
|
||||
#endif
|
||||
|
||||
#define SPI_CHECKSUM_START 0xABU
|
||||
#define SPI_SYNC_BYTE 0x5AU
|
||||
#define SPI_HACK 0x79U
|
||||
#define SPI_DACK 0x85U
|
||||
#define SPI_NACK 0x1FU
|
||||
|
||||
// SPI states
|
||||
enum {
|
||||
SPI_STATE_HEADER,
|
||||
SPI_STATE_HEADER_ACK,
|
||||
SPI_STATE_HEADER_NACK,
|
||||
SPI_STATE_DATA_RX,
|
||||
SPI_STATE_DATA_RX_ACK,
|
||||
SPI_STATE_DATA_TX
|
||||
};
|
||||
|
||||
uint16_t spi_error_count = 0;
|
||||
|
||||
#define SPI_HEADER_SIZE 7U
|
||||
|
||||
// low level SPI prototypes
|
||||
void llspi_init(void);
|
||||
void llspi_mosi_dma(uint8_t *addr, int len);
|
||||
void llspi_miso_dma(uint8_t *addr, int len);
|
||||
|
||||
static uint8_t spi_state = SPI_STATE_HEADER;
|
||||
static uint16_t spi_data_len_mosi;
|
||||
static bool spi_can_tx_ready = false;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/crc.h"
|
||||
|
||||
#define SPI_TIMEOUT_US 10000U
|
||||
|
||||
// got max rate from hitting a non-existent endpoint
|
||||
// in a tight loop, plus some buffer
|
||||
#define SPI_IRQ_RATE 16000U
|
||||
|
||||
#define SPI_BUF_SIZE 4096U
|
||||
// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2
|
||||
__attribute__((section(".sram12"))) extern uint8_t spi_buf_rx[SPI_BUF_SIZE];
|
||||
__attribute__((section(".sram12"))) extern uint8_t spi_buf_tx[SPI_BUF_SIZE];
|
||||
|
||||
#define SPI_CHECKSUM_START 0xABU
|
||||
#define SPI_SYNC_BYTE 0x5AU
|
||||
#define SPI_HACK 0x79U
|
||||
#define SPI_DACK 0x85U
|
||||
#define SPI_NACK 0x1FU
|
||||
|
||||
// SPI states
|
||||
enum {
|
||||
SPI_STATE_HEADER,
|
||||
SPI_STATE_HEADER_ACK,
|
||||
SPI_STATE_HEADER_NACK,
|
||||
SPI_STATE_DATA_RX,
|
||||
SPI_STATE_DATA_RX_ACK,
|
||||
SPI_STATE_DATA_TX
|
||||
};
|
||||
|
||||
extern uint16_t spi_error_count;
|
||||
|
||||
#define SPI_HEADER_SIZE 7U
|
||||
|
||||
// low level SPI prototypes
|
||||
void llspi_init(void);
|
||||
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);
|
||||
void spi_init(void);
|
||||
void spi_rx_done(void);
|
||||
void spi_tx_done(bool reset);
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "uart_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
// ***************************** Definitions *****************************
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// ***************************** Definitions *****************************
|
||||
#ifdef STM32H7
|
||||
#define FIFO_SIZE_INT 0x400U
|
||||
#else
|
||||
#define FIFO_SIZE_INT 0x200U
|
||||
#endif
|
||||
|
||||
typedef struct uart_ring {
|
||||
volatile uint16_t w_ptr_tx;
|
||||
volatile uint16_t r_ptr_tx;
|
||||
uint8_t *elems_tx;
|
||||
uint32_t tx_fifo_size;
|
||||
volatile uint16_t w_ptr_rx;
|
||||
volatile uint16_t r_ptr_rx;
|
||||
uint8_t *elems_rx;
|
||||
uint32_t rx_fifo_size;
|
||||
USART_TypeDef *uart;
|
||||
void (*callback)(struct uart_ring*);
|
||||
bool overwrite;
|
||||
} uart_ring;
|
||||
|
||||
// ***************************** Function prototypes *****************************
|
||||
void debug_ring_callback(uart_ring *ring);
|
||||
void uart_tx_ring(uart_ring *q);
|
||||
uart_ring *get_ring_by_number(int a);
|
||||
// ************************* Low-level buffer functions *************************
|
||||
bool get_char(uart_ring *q, char *elem);
|
||||
bool injectc(uart_ring *q, char elem);
|
||||
bool put_char(uart_ring *q, char elem);
|
||||
void clear_uart_buff(uart_ring *q);
|
||||
// ************************ High-level debug functions **********************
|
||||
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(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
|
||||
@@ -1,4 +1,87 @@
|
||||
#include "usb_declarations.h"
|
||||
#include "board/drivers/drivers.h"
|
||||
|
||||
// IRQs: OTG_FS
|
||||
|
||||
typedef union {
|
||||
uint16_t w;
|
||||
struct BW {
|
||||
uint8_t msb;
|
||||
uint8_t lsb;
|
||||
}
|
||||
bw;
|
||||
} uint16_t_uint8_t;
|
||||
|
||||
typedef union _USB_Setup {
|
||||
uint32_t d8[2];
|
||||
struct _SetupPkt_Struc
|
||||
{
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t_uint8_t wValue;
|
||||
uint16_t_uint8_t wIndex;
|
||||
uint16_t_uint8_t wLength;
|
||||
} b;
|
||||
} USB_Setup_TypeDef;
|
||||
|
||||
// **** supporting defines ****
|
||||
#define USB_REQ_GET_STATUS 0x00
|
||||
#define USB_REQ_SET_ADDRESS 0x05
|
||||
#define USB_REQ_GET_DESCRIPTOR 0x06
|
||||
#define USB_REQ_SET_CONFIGURATION 0x09
|
||||
#define USB_REQ_SET_INTERFACE 0x0B
|
||||
|
||||
#define USB_DESC_TYPE_DEVICE 0x01
|
||||
#define USB_DESC_TYPE_CONFIGURATION 0x02
|
||||
#define USB_DESC_TYPE_STRING 0x03
|
||||
#define USB_DESC_TYPE_INTERFACE 0x04
|
||||
#define USB_DESC_TYPE_ENDPOINT 0x05
|
||||
#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06
|
||||
#define USB_DESC_TYPE_BINARY_OBJECT_STORE 0x0f
|
||||
|
||||
// offsets for configuration strings
|
||||
#define STRING_OFFSET_LANGID 0x00
|
||||
#define STRING_OFFSET_IMANUFACTURER 0x01
|
||||
#define STRING_OFFSET_IPRODUCT 0x02
|
||||
#define STRING_OFFSET_ISERIAL 0x03
|
||||
#define STRING_OFFSET_ICONFIGURATION 0x04
|
||||
|
||||
// WinUSB requests
|
||||
#define WINUSB_REQ_GET_COMPATID_DESCRIPTOR 0x04
|
||||
#define WINUSB_REQ_GET_EXT_PROPS_OS 0x05
|
||||
#define WINUSB_REQ_GET_DESCRIPTOR 0x07
|
||||
|
||||
#define STS_DATA_UPDT 2
|
||||
#define STS_SETUP_UPDT 6
|
||||
|
||||
// for the repeating interfaces
|
||||
#define DSCR_INTERFACE_LEN 9
|
||||
#define DSCR_ENDPOINT_LEN 7
|
||||
#define DSCR_CONFIG_LEN 9
|
||||
#define DSCR_DEVICE_LEN 18
|
||||
|
||||
// endpoint types
|
||||
#define ENDPOINT_TYPE_BULK 2
|
||||
#define ENDPOINT_TYPE_INT 3
|
||||
|
||||
// These are arbitrary values used in bRequest
|
||||
#define MS_VENDOR_CODE 0x20
|
||||
#define WEBUSB_VENDOR_CODE 0x30
|
||||
|
||||
// BOS constants
|
||||
#define BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH 0x05
|
||||
#define BINARY_OBJECT_STORE_DESCRIPTOR 0x0F
|
||||
#define WINUSB_PLATFORM_DESCRIPTOR_LENGTH 0x9E
|
||||
|
||||
// Convert machine byte order to USB byte order
|
||||
#define TOUSBORDER(num)\
|
||||
((num) & 0xFFU), (((uint16_t)(num) >> 8) & 0xFFU)
|
||||
|
||||
// take in string length and return the first 2 bytes of a string descriptor
|
||||
#define STRING_DESCRIPTOR_HEADER(size)\
|
||||
(((((size) * 2) + 2) & 0xFF) | 0x0300)
|
||||
|
||||
#define ENDPOINT_RCV 0x80
|
||||
#define ENDPOINT_SND 0x00
|
||||
|
||||
static uint8_t response[USBPACKET_MAX_SIZE];
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// IRQs: OTG_FS
|
||||
|
||||
typedef union {
|
||||
uint16_t w;
|
||||
struct BW {
|
||||
uint8_t msb;
|
||||
uint8_t lsb;
|
||||
}
|
||||
bw;
|
||||
} uint16_t_uint8_t;
|
||||
|
||||
typedef union _USB_Setup {
|
||||
uint32_t d8[2];
|
||||
struct _SetupPkt_Struc
|
||||
{
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t_uint8_t wValue;
|
||||
uint16_t_uint8_t wIndex;
|
||||
uint16_t_uint8_t wLength;
|
||||
} b;
|
||||
} USB_Setup_TypeDef;
|
||||
|
||||
void usb_init(void);
|
||||
void refresh_can_tx_slots_available(void);
|
||||
|
||||
// **** supporting defines ****
|
||||
#define USB_REQ_GET_STATUS 0x00
|
||||
#define USB_REQ_CLEAR_FEATURE 0x01
|
||||
#define USB_REQ_SET_FEATURE 0x03
|
||||
#define USB_REQ_SET_ADDRESS 0x05
|
||||
#define USB_REQ_GET_DESCRIPTOR 0x06
|
||||
#define USB_REQ_SET_DESCRIPTOR 0x07
|
||||
#define USB_REQ_GET_CONFIGURATION 0x08
|
||||
#define USB_REQ_SET_CONFIGURATION 0x09
|
||||
#define USB_REQ_GET_INTERFACE 0x0A
|
||||
#define USB_REQ_SET_INTERFACE 0x0B
|
||||
#define USB_REQ_SYNCH_FRAME 0x0C
|
||||
|
||||
#define USB_DESC_TYPE_DEVICE 0x01
|
||||
#define USB_DESC_TYPE_CONFIGURATION 0x02
|
||||
#define USB_DESC_TYPE_STRING 0x03
|
||||
#define USB_DESC_TYPE_INTERFACE 0x04
|
||||
#define USB_DESC_TYPE_ENDPOINT 0x05
|
||||
#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06
|
||||
#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 0x07
|
||||
#define USB_DESC_TYPE_BINARY_OBJECT_STORE 0x0f
|
||||
|
||||
// offsets for configuration strings
|
||||
#define STRING_OFFSET_LANGID 0x00
|
||||
#define STRING_OFFSET_IMANUFACTURER 0x01
|
||||
#define STRING_OFFSET_IPRODUCT 0x02
|
||||
#define STRING_OFFSET_ISERIAL 0x03
|
||||
#define STRING_OFFSET_ICONFIGURATION 0x04
|
||||
#define STRING_OFFSET_IINTERFACE 0x05
|
||||
|
||||
// WebUSB requests
|
||||
#define WEBUSB_REQ_GET_URL 0x02
|
||||
|
||||
// WebUSB types
|
||||
#define WEBUSB_DESC_TYPE_URL 0x03
|
||||
#define WEBUSB_URL_SCHEME_HTTPS 0x01
|
||||
#define WEBUSB_URL_SCHEME_HTTP 0x00
|
||||
|
||||
// WinUSB requests
|
||||
#define WINUSB_REQ_GET_COMPATID_DESCRIPTOR 0x04
|
||||
#define WINUSB_REQ_GET_EXT_PROPS_OS 0x05
|
||||
#define WINUSB_REQ_GET_DESCRIPTOR 0x07
|
||||
|
||||
#define STS_GOUT_NAK 1
|
||||
#define STS_DATA_UPDT 2
|
||||
#define STS_XFER_COMP 3
|
||||
#define STS_SETUP_COMP 4
|
||||
#define STS_SETUP_UPDT 6
|
||||
|
||||
// for the repeating interfaces
|
||||
#define DSCR_INTERFACE_LEN 9
|
||||
#define DSCR_ENDPOINT_LEN 7
|
||||
#define DSCR_CONFIG_LEN 9
|
||||
#define DSCR_DEVICE_LEN 18
|
||||
|
||||
// endpoint types
|
||||
#define ENDPOINT_TYPE_CONTROL 0
|
||||
#define ENDPOINT_TYPE_ISO 1
|
||||
#define ENDPOINT_TYPE_BULK 2
|
||||
#define ENDPOINT_TYPE_INT 3
|
||||
|
||||
// These are arbitrary values used in bRequest
|
||||
#define MS_VENDOR_CODE 0x20
|
||||
#define WEBUSB_VENDOR_CODE 0x30
|
||||
|
||||
// BOS constants
|
||||
#define BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH 0x05
|
||||
#define BINARY_OBJECT_STORE_DESCRIPTOR 0x0F
|
||||
#define WINUSB_PLATFORM_DESCRIPTOR_LENGTH 0x9E
|
||||
|
||||
// Convert machine byte order to USB byte order
|
||||
#define TOUSBORDER(num)\
|
||||
((num) & 0xFFU), (((uint16_t)(num) >> 8) & 0xFFU)
|
||||
|
||||
// take in string length and return the first 2 bytes of a string descriptor
|
||||
#define STRING_DESCRIPTOR_HEADER(size)\
|
||||
(((((size) * 2) + 2) & 0xFF) | 0x0300)
|
||||
|
||||
#define ENDPOINT_RCV 0x80
|
||||
#define ENDPOINT_SND 0x00
|
||||
|
||||
// ***************************** USB port *****************************
|
||||
void can_tx_comms_resume_usb(void);
|
||||
@@ -5,8 +5,6 @@
|
||||
uint32_t *prog_ptr = NULL;
|
||||
bool unlocked = false;
|
||||
|
||||
void spi_init(void);
|
||||
|
||||
int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
|
||||
int resp_len = 0;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
// When changing these structs, python/__init__.py needs to be kept up to date!
|
||||
|
||||
#define HEALTH_PACKET_VERSION 17
|
||||
#define HEALTH_PACKET_VERSION 18
|
||||
struct __attribute__((packed)) health_t {
|
||||
uint32_t uptime_pkt;
|
||||
uint32_t voltage_pkt;
|
||||
@@ -27,6 +29,7 @@ struct __attribute__((packed)) health_t {
|
||||
uint16_t sbu1_voltage_mV;
|
||||
uint16_t sbu2_voltage_mV;
|
||||
uint8_t som_reset_triggered;
|
||||
uint16_t sound_output_level_pkt;
|
||||
};
|
||||
|
||||
#define CAN_HEALTH_PACKET_VERSION 5
|
||||
|
||||
@@ -52,7 +52,7 @@ class PandaJungle(Panda):
|
||||
|
||||
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"))
|
||||
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn.replace("panda", "panda_jungle"))
|
||||
super().flash(fn=fn, code=code, reconnect=reconnect)
|
||||
|
||||
def recover(self, timeout: int | None = 60, reset: bool = True) -> bool:
|
||||
@@ -73,15 +73,9 @@ class PandaJungle(Panda):
|
||||
self.flash()
|
||||
return True
|
||||
|
||||
def get_mcu_type(self) -> McuType:
|
||||
hw_type = self.get_type()
|
||||
if 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"))
|
||||
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn.replace("panda", "panda_jungle"))
|
||||
return super().up_to_date(fn=fn)
|
||||
|
||||
# ******************* health *******************
|
||||
|
||||
22
board/main.c
22
board/main.c
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "board/drivers/fdcan.h"
|
||||
|
||||
#include "board/power_saving.h"
|
||||
#include "board/sys/power_saving.h"
|
||||
|
||||
#include "board/obj/gitversion.h"
|
||||
|
||||
@@ -148,7 +148,7 @@ static void tick_handler(void) {
|
||||
// re-init everything that uses harness status
|
||||
can_init_all();
|
||||
set_safety_mode(current_safety_mode, current_safety_param);
|
||||
set_power_save_state(power_save_status);
|
||||
set_power_save_state(power_save_enabled);
|
||||
}
|
||||
|
||||
// decimated to 1Hz
|
||||
@@ -167,7 +167,7 @@ static void tick_handler(void) {
|
||||
|
||||
// turn off the blue LED, turned on by CAN
|
||||
// unless we are in power saving mode
|
||||
led_set(LED_BLUE, (uptime_cnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED));
|
||||
led_set(LED_BLUE, (uptime_cnt & 1U) && power_save_enabled);
|
||||
|
||||
const bool recent_heartbeat = heartbeat_counter == 0U;
|
||||
|
||||
@@ -233,8 +233,8 @@ static void tick_handler(void) {
|
||||
set_safety_mode(SAFETY_SILENT, 0U);
|
||||
}
|
||||
|
||||
if (power_save_status != POWER_SAVE_STATUS_ENABLED) {
|
||||
set_power_save_state(POWER_SAVE_STATUS_ENABLED);
|
||||
if (!power_save_enabled) {
|
||||
set_power_save_state(true);
|
||||
}
|
||||
|
||||
// Also disable IR when the heartbeat goes missing
|
||||
@@ -343,7 +343,12 @@ int main(void) {
|
||||
|
||||
// LED should keep on blinking all the time
|
||||
while (true) {
|
||||
if (power_save_status == POWER_SAVE_STATUS_DISABLED) {
|
||||
#ifdef ALLOW_DEBUG
|
||||
if (stop_mode_requested) {
|
||||
enter_stop_mode();
|
||||
}
|
||||
#endif
|
||||
if (!power_save_enabled) {
|
||||
#ifdef DEBUG_FAULTS
|
||||
if (fault_status == FAULT_STATUS_NONE) {
|
||||
#endif
|
||||
@@ -371,6 +376,11 @@ int main(void) {
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if ((hw_type == HW_TYPE_CUATRO) && !current_board->read_som_gpio()) {
|
||||
assert_fatal(current_safety_mode == SAFETY_SILENT, "Error: Entering low power mode while not in SAFETY_SILENT. Hanging\n");
|
||||
enter_stop_mode(); // deep sleep, wakes on CAN or SBU activity
|
||||
assert_fatal(false, "Error: enter_stop_mode returned after system reset. Hanging\n");
|
||||
}
|
||||
__WFI();
|
||||
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ static int get_health_pkt(void *dat) {
|
||||
health->safety_mode_pkt = (uint8_t)(current_safety_mode);
|
||||
health->safety_param_pkt = current_safety_param;
|
||||
health->alternative_experience_pkt = alternative_experience;
|
||||
health->power_save_enabled_pkt = power_save_status == POWER_SAVE_STATUS_ENABLED;
|
||||
health->power_save_enabled_pkt = power_save_enabled;
|
||||
health->heartbeat_lost_pkt = heartbeat_lost;
|
||||
health->safety_rx_checks_invalid_pkt = safety_rx_checks_invalid;
|
||||
|
||||
@@ -42,6 +42,8 @@ static int get_health_pkt(void *dat) {
|
||||
|
||||
health->som_reset_triggered = bootkick_reset_triggered;
|
||||
|
||||
health->sound_output_level_pkt = sound_output_level;
|
||||
|
||||
return sizeof(*health);
|
||||
}
|
||||
|
||||
@@ -95,6 +97,14 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
|
||||
resp[1] = ((fan_state.rpm & 0xFF00U) >> 8U);
|
||||
resp_len = 2;
|
||||
break;
|
||||
// **** 0xb5: request deep sleep, wakes on CAN or SBU
|
||||
#ifdef ALLOW_DEBUG
|
||||
case 0xb5:
|
||||
set_safety_mode(SAFETY_SILENT, 0U);
|
||||
set_power_save_state(true);
|
||||
stop_mode_requested = true;
|
||||
break;
|
||||
#endif
|
||||
// **** 0xc0: reset communications state
|
||||
case 0xc0:
|
||||
comms_can_reset();
|
||||
@@ -265,7 +275,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
|
||||
break;
|
||||
// **** 0xe7: set power save state
|
||||
case 0xe7:
|
||||
set_power_save_state(req->param1);
|
||||
set_power_save_state(req->param1 != 0U);
|
||||
break;
|
||||
// **** 0xe8: set can-fd auto swithing mode
|
||||
case 0xe8:
|
||||
|
||||
@@ -20,3 +20,6 @@ extern bool heartbeat_disabled;
|
||||
|
||||
// siren state
|
||||
extern bool siren_enabled;
|
||||
|
||||
// sound
|
||||
extern uint16_t sound_output_level;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#include "power_saving_declarations.h"
|
||||
|
||||
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes.
|
||||
// See rule: CoU_3
|
||||
|
||||
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 = (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);
|
||||
}
|
||||
}
|
||||
|
||||
void set_power_save_state(int state) {
|
||||
bool is_valid_state = (state == POWER_SAVE_STATUS_ENABLED) || (state == POWER_SAVE_STATUS_DISABLED);
|
||||
if (is_valid_state && (state != power_save_status)) {
|
||||
bool enable = false;
|
||||
if (state == POWER_SAVE_STATUS_ENABLED) {
|
||||
print("enable power savings\n");
|
||||
|
||||
// Disable CAN interrupts
|
||||
if (harness.status == HARNESS_STATUS_FLIPPED) {
|
||||
llcan_irq_disable(cans[0]);
|
||||
} else {
|
||||
llcan_irq_disable(cans[2]);
|
||||
}
|
||||
llcan_irq_disable(cans[1]);
|
||||
} else {
|
||||
print("disable power savings\n");
|
||||
|
||||
if (harness.status == HARNESS_STATUS_FLIPPED) {
|
||||
llcan_irq_enable(cans[0]);
|
||||
} else {
|
||||
llcan_irq_enable(cans[2]);
|
||||
}
|
||||
llcan_irq_enable(cans[1]);
|
||||
|
||||
enable = true;
|
||||
}
|
||||
|
||||
enable_can_transceivers(enable);
|
||||
|
||||
// Switch off IR when in power saving
|
||||
if(!enable){
|
||||
current_board->set_ir_power(0U);
|
||||
}
|
||||
|
||||
power_save_status = state;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes.
|
||||
// See rule: CoU_3
|
||||
|
||||
#define POWER_SAVE_STATUS_DISABLED 0
|
||||
#define POWER_SAVE_STATUS_ENABLED 1
|
||||
|
||||
extern int power_save_status;
|
||||
|
||||
void set_power_save_state(int state);
|
||||
@@ -12,6 +12,7 @@ __attribute__((section(".sram4"))) static uint16_t mic_tx_buf[2][MIC_TX_BUF_SIZE
|
||||
static uint8_t sound_idle_count;
|
||||
static uint8_t mic_idle_count;
|
||||
static uint8_t mic_buffer_count;
|
||||
uint16_t sound_output_level;
|
||||
|
||||
void sound_tick(void) {
|
||||
if (sound_idle_count > 0U) {
|
||||
@@ -19,6 +20,7 @@ void sound_tick(void) {
|
||||
if (sound_idle_count == 0U) {
|
||||
current_board->set_amp_enabled(false);
|
||||
register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN);
|
||||
sound_output_level = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,14 +74,31 @@ static void BDMA_Channel0_IRQ_Handler(void) {
|
||||
|
||||
// process samples (shift to 12b and bias to be unsigned)
|
||||
bool sound_playing = false;
|
||||
uint32_t abs_sum = 0U;
|
||||
|
||||
for (uint16_t i=0U; i < SOUND_RX_BUF_SIZE; i += 2U) {
|
||||
// since we are playing mono and receiving stereo, we take every other sample
|
||||
sound_tx_buf[playback_buf][i/2U] = ((sound_rx_buf[rx_buf_idx][i] + (1UL << 14)) >> 3);
|
||||
uint16_t sample = ((sound_rx_buf[rx_buf_idx][i] + (1UL << 14)) >> 3) & 0xFFFU;
|
||||
sound_tx_buf[playback_buf][i/2U] = sample;
|
||||
if (sound_rx_buf[rx_buf_idx][i] > 0U) {
|
||||
sound_playing = true;
|
||||
}
|
||||
|
||||
// this assumes all audio is "zero" centered
|
||||
if (sample > 0x7FFU) {
|
||||
abs_sum += (uint32_t)sample - 0x7FFU;
|
||||
} else {
|
||||
abs_sum += 0x7FFU - (uint32_t)sample;
|
||||
}
|
||||
}
|
||||
|
||||
// VU meter: fast attack, slow decay (~460ms half-life at ~96Hz ISR rate)
|
||||
uint16_t level = (uint16_t)(abs_sum / (SOUND_RX_BUF_SIZE / 2U));
|
||||
if (level >= sound_output_level) {
|
||||
sound_output_level = level;
|
||||
}
|
||||
sound_output_level -= (sound_output_level >> 6U);
|
||||
|
||||
// manage amp state
|
||||
if (sound_playing) {
|
||||
if (sound_idle_count == 0U) {
|
||||
@@ -102,8 +121,6 @@ static void BDMA_Channel0_IRQ_Handler(void) {
|
||||
DFSDM1_Filter0->FLTCR1 |= DFSDM_FLTCR1_RSWSTART;
|
||||
}
|
||||
mic_idle_count = SOUND_IDLE_TIMEOUT;
|
||||
|
||||
sound_tick();
|
||||
}
|
||||
|
||||
void sound_init_dac(void) {
|
||||
|
||||
@@ -53,12 +53,16 @@ separate IRQs for RX and TX.
|
||||
#endif
|
||||
|
||||
#include "board/libc.h"
|
||||
#include "board/critical.h"
|
||||
#include "board/faults.h"
|
||||
#include "board/sys/critical.h"
|
||||
#include "board/sys/faults.h"
|
||||
#include "board/utils.h"
|
||||
|
||||
#include "board/drivers/registers.h"
|
||||
#include "board/drivers/interrupts.h"
|
||||
|
||||
#ifdef BOOTSTUB
|
||||
uart_ring uart_ring_som_debug;
|
||||
#endif
|
||||
#include "board/drivers/gpio.h"
|
||||
#include "board/stm32h7/peripherals.h"
|
||||
#include "board/stm32h7/interrupt_handlers.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "critical_declarations.h"
|
||||
#include "board/sys/sys.h"
|
||||
|
||||
// ********************* Critical section helpers *********************
|
||||
uint8_t global_critical_depth = 0U;
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "faults_declarations.h"
|
||||
#include "board/sys/sys.h"
|
||||
|
||||
uint8_t fault_status = FAULT_STATUS_NONE;
|
||||
uint32_t faults = 0U;
|
||||
148
board/sys/power_saving.h
Normal file
148
board/sys/power_saving.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "board/sys/sys.h"
|
||||
|
||||
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM2331, we should never use any of the available hardware low power modes during safety function execution.
|
||||
// See rule: CoU_3
|
||||
|
||||
// Low power state "stop mode" is only entered from SAFETY_SILENT when no safety function is active and exited via reset which is a safe state.
|
||||
|
||||
bool power_save_enabled = false;
|
||||
#ifdef ALLOW_DEBUG
|
||||
volatile bool stop_mode_requested = false;
|
||||
#endif
|
||||
|
||||
void enable_can_transceivers(bool enabled) {
|
||||
// Leave main CAN always on for CAN-based ignition detection
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void set_power_save_state(bool enable) {
|
||||
if (enable != power_save_enabled) {
|
||||
if (enable) {
|
||||
print("enable power savings\n");
|
||||
|
||||
// Disable CAN interrupts
|
||||
if (harness.status == HARNESS_STATUS_FLIPPED) {
|
||||
llcan_irq_disable(cans[0]);
|
||||
} else {
|
||||
llcan_irq_disable(cans[2]);
|
||||
}
|
||||
llcan_irq_disable(cans[1]);
|
||||
} else {
|
||||
print("disable power savings\n");
|
||||
|
||||
if (harness.status == HARNESS_STATUS_FLIPPED) {
|
||||
llcan_irq_enable(cans[0]);
|
||||
} else {
|
||||
llcan_irq_enable(cans[2]);
|
||||
}
|
||||
llcan_irq_enable(cans[1]);
|
||||
}
|
||||
|
||||
enable_can_transceivers(!enable);
|
||||
|
||||
// Switch off IR when in power saving
|
||||
if(enable){
|
||||
current_board->set_ir_power(0U);
|
||||
}
|
||||
|
||||
power_save_enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
static void enter_stop_mode(void) {
|
||||
// set all GPIO to analog mode to reduce power, analog mode also disables pull resistors
|
||||
register_set(&(GPIOA->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOB->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOC->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOD->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOE->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOF->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
register_set(&(GPIOG->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
|
||||
|
||||
// init GPIO to lowest power state
|
||||
current_board->set_bootkick(BOOT_STANDBY);
|
||||
current_board->set_amp_enabled(false);
|
||||
for (uint8_t i = 1U; i <= 4U; i++) {
|
||||
current_board->enable_can_transceiver(i, false);
|
||||
}
|
||||
|
||||
// disable ADCs
|
||||
ADC1->CR &= ~(ADC_CR_ADEN);
|
||||
ADC1->CR |= ADC_CR_DEEPPWD;
|
||||
ADC2->CR &= ~(ADC_CR_ADEN);
|
||||
ADC2->CR |= ADC_CR_DEEPPWD;
|
||||
|
||||
// disable HSI48: 48 MHz USB clock
|
||||
register_clear_bits(&(RCC->CR), RCC_CR_HSI48ON);
|
||||
// disable SRAM retention in stop mode
|
||||
register_clear_bits(&(RCC->AHB2LPENR), RCC_AHB2LPENR_SRAM1LPEN | RCC_AHB2LPENR_SRAM2LPEN);
|
||||
register_clear_bits(&(RCC->AHB4LPENR), RCC_AHB4LPENR_SRAM4LPEN);
|
||||
register_clear_bits(&(RCC->AHB3LPENR), RCC_AHB3LPENR_AXISRAMLPEN);
|
||||
|
||||
// SBU pins to input for EXTI wakeup
|
||||
set_gpio_mode(current_board->harness_config->GPIO_SBU1,
|
||||
current_board->harness_config->pin_SBU1, MODE_INPUT);
|
||||
set_gpio_mode(current_board->harness_config->GPIO_SBU2,
|
||||
current_board->harness_config->pin_SBU2, MODE_INPUT);
|
||||
|
||||
// EXTI1: SBU2 (PA1)
|
||||
// EXTI4: SBU1 (PC4)
|
||||
register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI1_PA, 0xF0U);
|
||||
register_set(&(SYSCFG->EXTICR[1]), SYSCFG_EXTICR2_EXTI4_PC, 0xFU);
|
||||
register_set_bits(&(EXTI->IMR1), (1U << 1) | (1U << 4));
|
||||
register_set_bits(&(EXTI->RTSR1), (1U << 1) | (1U << 4));
|
||||
register_set_bits(&(EXTI->FTSR1), (1U << 1) | (1U << 4));
|
||||
|
||||
// EXTI for CAN wakeup
|
||||
// EXTI8: FDCAN1 RX (PB8)
|
||||
// EXTI5: FDCAN2 RX (PB5)
|
||||
// EXTI12: FDCAN3 RX (PD12)
|
||||
set_gpio_mode(GPIOB, 8, MODE_INPUT);
|
||||
register_set(&(SYSCFG->EXTICR[2]), SYSCFG_EXTICR3_EXTI8_PB, 0xFU);
|
||||
set_gpio_mode(GPIOB, 5, MODE_INPUT);
|
||||
register_set(&(SYSCFG->EXTICR[1]), SYSCFG_EXTICR2_EXTI5_PB, 0xF0U);
|
||||
set_gpio_mode(GPIOD, 12, MODE_INPUT);
|
||||
register_set(&(SYSCFG->EXTICR[3]), SYSCFG_EXTICR4_EXTI12_PD, 0xFU);
|
||||
uint32_t can_exti_line = (1UL << 8) | (1UL << 5) | (1UL << 12);
|
||||
register_set_bits(&(EXTI->IMR1), can_exti_line);
|
||||
register_set_bits(&(EXTI->FTSR1), can_exti_line);
|
||||
|
||||
// clear pending EXTI
|
||||
EXTI->PR1 = (1U << 1) | (1U << 4) | can_exti_line;
|
||||
|
||||
// reset if ignition just came on before going to sleep
|
||||
if (harness_check_ignition()) {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
// stop mode
|
||||
register_clear_bits(&(PWR->CPUCR), PWR_CPUCR_PDDS_D1 | PWR_CPUCR_PDDS_D2 | PWR_CPUCR_PDDS_D3);
|
||||
|
||||
// set SVOS5 voltage scaling, flash low-power
|
||||
register_set(&(PWR->CR1), PWR_CR1_SVOS_0 | PWR_CR1_FLPS, PWR_CR1_SVOS | PWR_CR1_FLPS);
|
||||
|
||||
// enter stop mode on WFI
|
||||
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
||||
|
||||
__disable_irq();
|
||||
|
||||
// disable all NVIC interrupts and clear pending
|
||||
for (uint32_t i = 0U; i < 8U; i++) {
|
||||
NVIC->ICER[i] = 0xFFFFFFFFU;
|
||||
NVIC->ICPR[i] = 0xFFFFFFFFU;
|
||||
}
|
||||
// enable only wakeup EXTI interrupts
|
||||
NVIC_EnableIRQ(EXTI1_IRQn); // SBU2 (PA1)
|
||||
NVIC_EnableIRQ(EXTI4_IRQn); // SBU1 (PC4)
|
||||
NVIC_EnableIRQ(EXTI9_5_IRQn); // FDCAN1 RX (PB8), FDCAN2 RX (PB5)
|
||||
NVIC_EnableIRQ(EXTI15_10_IRQn); // FDCAN3 RX (PD12)
|
||||
|
||||
__DSB();
|
||||
__ISB();
|
||||
__WFI();
|
||||
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
@@ -1,5 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// ******************** critical ********************
|
||||
|
||||
void enable_interrupts(void);
|
||||
void disable_interrupts(void);
|
||||
|
||||
extern uint8_t global_critical_depth;
|
||||
|
||||
#ifndef ENTER_CRITICAL
|
||||
#define ENTER_CRITICAL() \
|
||||
__disable_irq(); \
|
||||
global_critical_depth += 1U;
|
||||
#endif
|
||||
|
||||
#ifndef EXIT_CRITICAL
|
||||
#define EXIT_CRITICAL() \
|
||||
global_critical_depth -= 1U; \
|
||||
if ((global_critical_depth == 0U) && interrupts_enabled) { \
|
||||
__enable_irq(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
// ******************** faults ********************
|
||||
|
||||
#define FAULT_STATUS_NONE 0U
|
||||
#define FAULT_STATUS_TEMPORARY 1U
|
||||
#define FAULT_STATUS_PERMANENT 2U
|
||||
@@ -32,3 +55,9 @@ extern uint32_t faults;
|
||||
|
||||
void fault_occurred(uint32_t fault);
|
||||
void fault_recovered(uint32_t fault);
|
||||
|
||||
// ******************** power_saving ********************
|
||||
|
||||
extern bool power_save_enabled;
|
||||
|
||||
void set_power_save_state(bool enable);
|
||||
@@ -30,6 +30,8 @@ dev = [
|
||||
"mypy",
|
||||
"setuptools",
|
||||
"spidev; platform_system == 'Linux'",
|
||||
"gcc-arm-none-eabi @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=gcc-arm-none-eabi",
|
||||
"cppcheck @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=cppcheck",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
@@ -37,7 +39,13 @@ requires = ["setuptools>=61", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["panda"]
|
||||
packages = [
|
||||
"panda",
|
||||
"panda.python",
|
||||
"panda.board",
|
||||
"panda.board.body",
|
||||
"panda.board.jungle",
|
||||
]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
panda = "."
|
||||
|
||||
@@ -111,20 +111,17 @@ class Panda:
|
||||
|
||||
# from https://github.com/commaai/openpilot/blob/103b4df18cbc38f4129555ab8b15824d1a672bdf/cereal/log.capnp#L648
|
||||
HW_TYPE_UNKNOWN = b'\x00'
|
||||
HW_TYPE_WHITE = b'\x01'
|
||||
HW_TYPE_BLACK = b'\x03'
|
||||
HW_TYPE_RED_PANDA = b'\x07'
|
||||
HW_TYPE_TRES = b'\x09'
|
||||
HW_TYPE_CUATRO = b'\x0a'
|
||||
HW_TYPE_BODY = b'\xb1'
|
||||
|
||||
CAN_PACKET_VERSION = 4
|
||||
HEALTH_PACKET_VERSION = 17
|
||||
HEALTH_PACKET_VERSION = 18
|
||||
CAN_HEALTH_PACKET_VERSION = 5
|
||||
HEALTH_STRUCT = struct.Struct("<IIIIIIIIBBBBBHBBBHfBBHHHB")
|
||||
HEALTH_STRUCT = struct.Struct("<IIIIIIIIBBBBBHBBBHfBBHHHBH")
|
||||
CAN_HEALTH_STRUCT = struct.Struct("<BIBBBBBBBBIIIIIIIHHBBBIIII")
|
||||
|
||||
F4_DEVICES = [HW_TYPE_WHITE, HW_TYPE_BLACK]
|
||||
H7_DEVICES = [HW_TYPE_RED_PANDA, HW_TYPE_TRES, HW_TYPE_CUATRO, HW_TYPE_BODY]
|
||||
SUPPORTED_DEVICES = H7_DEVICES
|
||||
|
||||
@@ -147,7 +144,6 @@ class Panda:
|
||||
else:
|
||||
self._connect_serial = serial
|
||||
|
||||
# connect and set mcu type
|
||||
self.connect(claim)
|
||||
|
||||
def _cli_select_panda(self):
|
||||
@@ -204,14 +200,9 @@ class Panda:
|
||||
self._serial = serial
|
||||
self._connect_serial = serial
|
||||
self._handle_open = True
|
||||
self._mcu_type = self.get_mcu_type()
|
||||
self.health_version, self.can_version, self.can_health_version = self.get_packets_versions()
|
||||
logger.debug("connected")
|
||||
|
||||
hw_type = self.get_type()
|
||||
if hw_type not in self.SUPPORTED_DEVICES:
|
||||
print("WARNING: Using deprecated HW")
|
||||
|
||||
# disable openpilot's heartbeat checks
|
||||
if self._disable_checks:
|
||||
self.set_heartbeat_disabled()
|
||||
@@ -338,6 +329,9 @@ class Panda:
|
||||
return []
|
||||
|
||||
def reset(self, enter_bootstub=False, enter_bootloader=False, reconnect=True):
|
||||
if enter_bootstub or enter_bootloader:
|
||||
assert (hw_type := self.get_type()) in self.SUPPORTED_DEVICES, f"Unknown HW: {hw_type}"
|
||||
|
||||
# no response is expected since it resets right away
|
||||
timeout = 5000 if isinstance(self._handle, PandaSpiHandle) else 15000
|
||||
try:
|
||||
@@ -417,16 +411,14 @@ class Panda:
|
||||
pass
|
||||
|
||||
def flash(self, fn=None, code=None, reconnect=True):
|
||||
assert (hw_type := self.get_type()) in self.SUPPORTED_DEVICES, f"Unknown HW: {hw_type}"
|
||||
|
||||
if self.up_to_date(fn=fn):
|
||||
logger.info("flash: already up to date")
|
||||
return
|
||||
|
||||
hw_type = self.get_type()
|
||||
if hw_type not in self.SUPPORTED_DEVICES:
|
||||
raise RuntimeError(f"HW type {hw_type.hex()} is deprecated and can no longer be flashed.")
|
||||
|
||||
if not fn:
|
||||
fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn)
|
||||
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn)
|
||||
assert os.path.isfile(fn)
|
||||
logger.debug("flash: main version is %s", self.get_version())
|
||||
if not self.bootstub:
|
||||
@@ -441,7 +433,7 @@ class Panda:
|
||||
logger.debug("flash: bootstub version is %s", self.get_version())
|
||||
|
||||
# do flash
|
||||
Panda.flash_static(self._handle, code, mcu_type=self._mcu_type)
|
||||
Panda.flash_static(self._handle, code, mcu_type=McuType.H7)
|
||||
|
||||
# reconnect
|
||||
if reconnect:
|
||||
@@ -492,7 +484,7 @@ class Panda:
|
||||
def up_to_date(self, fn=None) -> bool:
|
||||
current = self.get_signature()
|
||||
if fn is None:
|
||||
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn)
|
||||
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn)
|
||||
expected = Panda.get_signature_from_firmware(fn)
|
||||
return (current == expected)
|
||||
|
||||
@@ -531,6 +523,7 @@ class Panda:
|
||||
"sbu1_voltage_mV": a[22],
|
||||
"sbu2_voltage_mV": a[23],
|
||||
"som_reset_triggered": a[24],
|
||||
"sound_output_level": a[25],
|
||||
}
|
||||
|
||||
@ensure_can_health_packet_version
|
||||
@@ -604,14 +597,6 @@ class Panda:
|
||||
else:
|
||||
return (0, 0, 0)
|
||||
|
||||
def get_mcu_type(self) -> McuType:
|
||||
hw_type = self.get_type()
|
||||
if hw_type in Panda.F4_DEVICES:
|
||||
return McuType.F4
|
||||
elif hw_type in Panda.H7_DEVICES:
|
||||
return McuType.H7
|
||||
raise ValueError(f"unknown HW type: {hw_type}")
|
||||
|
||||
def is_internal(self):
|
||||
return self.get_type() in Panda.INTERNAL_DEVICES
|
||||
|
||||
@@ -632,7 +617,7 @@ class Panda:
|
||||
return self._serial
|
||||
|
||||
def get_dfu_serial(self):
|
||||
return PandaDFU.st_serial_to_dfu_serial(self._serial, self._mcu_type)
|
||||
return PandaDFU.st_serial_to_dfu_serial(self._serial, McuType.H7)
|
||||
|
||||
def get_uid(self):
|
||||
"""
|
||||
@@ -656,6 +641,9 @@ class Panda:
|
||||
def set_power_save(self, power_save_enabled=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe7, int(power_save_enabled), 0, b'')
|
||||
|
||||
def enter_stop_mode(self):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xb5, 0, 0, b'', expect_disconnect=True)
|
||||
|
||||
def set_safety_mode(self, mode=CarParams.SafetyModel.silent, param=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, param, b'')
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import binascii
|
||||
import os
|
||||
import fcntl
|
||||
import math
|
||||
import time
|
||||
import struct
|
||||
@@ -12,6 +11,13 @@ from .base import BaseHandle, BaseSTBootloaderHandle, TIMEOUT
|
||||
from .constants import McuType, MCU_TYPE_BY_IDCODE, USBPACKET_MAX_SIZE
|
||||
from .utils import logger
|
||||
|
||||
# No fcntl on Windows
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None # type: ignore
|
||||
|
||||
# No spidev on MacOS/Windows
|
||||
try:
|
||||
import spidev
|
||||
except ImportError:
|
||||
|
||||
8
setup.sh
8
setup.sh
@@ -8,9 +8,8 @@ PLATFORM=$(uname -s)
|
||||
|
||||
echo "installing dependencies"
|
||||
if [[ $PLATFORM == "Darwin" ]]; then
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
brew install --cask gcc-arm-embedded
|
||||
brew install python3 gcc@13
|
||||
# pass
|
||||
:
|
||||
elif [[ $PLATFORM == "Linux" ]]; then
|
||||
# for AGNOS since we clear the apt lists
|
||||
if [[ ! -d /"var/lib/apt/" ]]; then
|
||||
@@ -19,9 +18,8 @@ elif [[ $PLATFORM == "Linux" ]]; then
|
||||
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
curl ca-certificates \
|
||||
make g++ git libnewlib-arm-none-eabi \
|
||||
make g++ git \
|
||||
libusb-1.0-0 \
|
||||
gcc-arm-none-eabi \
|
||||
python3-dev python3-pip python3-venv
|
||||
else
|
||||
echo "WARNING: unsupported platform. skipping apt/brew install."
|
||||
|
||||
@@ -11,7 +11,6 @@ def check_signature(p):
|
||||
|
||||
|
||||
def test_dfu(p):
|
||||
app_mcu_type = p.get_mcu_type()
|
||||
dfu_serial = p.get_dfu_serial()
|
||||
|
||||
p.reset(enter_bootstub=True)
|
||||
@@ -19,7 +18,7 @@ def test_dfu(p):
|
||||
assert Panda.wait_for_dfu(dfu_serial, timeout=19), "failed to enter DFU"
|
||||
|
||||
dfu = PandaDFU(dfu_serial)
|
||||
assert dfu.get_mcu_type() == app_mcu_type
|
||||
assert dfu.get_mcu_type() == McuType.H7
|
||||
|
||||
assert dfu_serial in PandaDFU.list()
|
||||
|
||||
@@ -38,9 +37,9 @@ def test_known_bootstub(p):
|
||||
McuType.H7: ["bootstub.panda_h7.bin"],
|
||||
}
|
||||
|
||||
for kb in known_bootstubs[p.get_mcu_type()]:
|
||||
app_ids = (p.get_mcu_type(), p.get_usb_serial())
|
||||
assert None not in app_ids
|
||||
for kb in known_bootstubs[McuType.H7]:
|
||||
app_serial = p.get_usb_serial()
|
||||
assert app_serial is not None
|
||||
|
||||
p.reset(enter_bootstub=True)
|
||||
p.reset(enter_bootloader=True)
|
||||
@@ -57,10 +56,9 @@ def test_known_bootstub(p):
|
||||
|
||||
p.connect(claim=False, wait=True)
|
||||
|
||||
# check for MCU or serial mismatch
|
||||
# check for serial mismatch
|
||||
with Panda(p._serial, claim=False) as np:
|
||||
bootstub_ids = (np.get_mcu_type(), np.get_usb_serial())
|
||||
assert app_ids == bootstub_ids
|
||||
assert np.get_usb_serial() == app_serial
|
||||
|
||||
# ensure we can flash app and it jumps to app
|
||||
p.flash()
|
||||
|
||||
@@ -17,8 +17,6 @@ def test_hw_type(p):
|
||||
"""
|
||||
|
||||
hw_type = p.get_type()
|
||||
mcu_type = p.get_mcu_type()
|
||||
assert mcu_type is not None
|
||||
|
||||
app_uid = p.get_uid()
|
||||
usb_serial = p.get_usb_serial()
|
||||
@@ -30,7 +28,6 @@ def test_hw_type(p):
|
||||
with Panda(p.get_usb_serial()) as pp:
|
||||
assert pp.bootstub
|
||||
assert pp.get_type() == hw_type, "Bootstub and app hw type mismatch"
|
||||
assert pp.get_mcu_type() == mcu_type, "Bootstub and app MCU type mismatch"
|
||||
assert pp.get_uid() == app_uid
|
||||
|
||||
def test_heartbeat(p, panda_jungle):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import time
|
||||
import pytest
|
||||
|
||||
from panda import Panda
|
||||
|
||||
@@ -11,3 +12,33 @@ def test_boot_time(p):
|
||||
# USB enumeration is slow, so SPI is faster
|
||||
assert time.monotonic() - st < (1.0 if p.spi else 5.0)
|
||||
|
||||
@pytest.mark.panda_expect_can_error
|
||||
@pytest.mark.test_panda_types((Panda.HW_TYPE_CUATRO, ))
|
||||
def test_stop_mode(p, panda_jungle):
|
||||
serial = p.get_usb_serial()
|
||||
panda_jungle.set_obd(True)
|
||||
|
||||
for orientation in (Panda.HARNESS_STATUS_FLIPPED, Panda.HARNESS_STATUS_NORMAL):
|
||||
panda_jungle.set_harness_orientation(orientation)
|
||||
time.sleep(0.25) # wait for orientation detection
|
||||
|
||||
for wakeup in ("ign", "0", "1", "2"):
|
||||
panda_jungle.set_ignition(False)
|
||||
print(f"orientation={orientation} wakeup={wakeup}")
|
||||
|
||||
p.enter_stop_mode()
|
||||
p.close()
|
||||
# wait for panda to enter stop mode
|
||||
time.sleep(1.5)
|
||||
|
||||
# wake via ignition or CAN activity
|
||||
if wakeup == "ign":
|
||||
panda_jungle.set_ignition(True)
|
||||
else:
|
||||
panda_jungle.can_send(0x123, b'\x01\x02', int(wakeup))
|
||||
|
||||
# panda should reset and come back
|
||||
assert Panda.wait_for_panda(serial, timeout=10)
|
||||
|
||||
p.reconnect()
|
||||
assert p.health()['uptime'] < 3
|
||||
|
||||
@@ -148,6 +148,7 @@ def fixture_panda_setup(request):
|
||||
init_jungle()
|
||||
|
||||
# init panda
|
||||
assert Panda.wait_for_panda(_panda_serial, timeout=10), "panda not found"
|
||||
p = Panda(serial=_panda_serial)
|
||||
p.reset(reconnect=True)
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ def recover(s):
|
||||
def flash(s):
|
||||
with PandaJungle(s) as p:
|
||||
p.flash()
|
||||
return p.get_mcu_type()
|
||||
|
||||
# Reset + flash all CI hardware to get it into a consistent state
|
||||
# * port 1: jungles-under-test
|
||||
@@ -42,5 +41,4 @@ if __name__ == "__main__":
|
||||
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.H7, }
|
||||
list(exc.map(flash, SERIALS, timeout=20))
|
||||
|
||||
@@ -12,7 +12,7 @@ void can_tx_comms_resume_usb(void) { };
|
||||
void can_tx_comms_resume_spi(void) { };
|
||||
|
||||
#include "health.h"
|
||||
#include "faults.h"
|
||||
#include "sys/faults.h"
|
||||
#include "libc.h"
|
||||
#include "boards/board_declarations.h"
|
||||
#include "opendbc/safety/safety.h"
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
: "${CPPCHECK_DIR:=$DIR/cppcheck/}"
|
||||
|
||||
if [ ! -d "$CPPCHECK_DIR" ]; then
|
||||
git clone https://github.com/danmar/cppcheck.git $CPPCHECK_DIR
|
||||
fi
|
||||
|
||||
cd $CPPCHECK_DIR
|
||||
|
||||
VERS="2.16.0"
|
||||
git fetch --all --tags --force
|
||||
git checkout $VERS
|
||||
|
||||
#make clean
|
||||
make MATCHCOMPILTER=yes CXXFLAGS="-O2" -j8
|
||||
@@ -10,12 +10,7 @@ YELLOW="\e[1;33m"
|
||||
RED="\e[1;31m"
|
||||
NC='\033[0m'
|
||||
|
||||
: "${CPPCHECK_DIR:=$DIR/cppcheck/}"
|
||||
|
||||
# install cppcheck if missing
|
||||
if [ -z "${SKIP_CPPCHECK_INSTALL}" ]; then
|
||||
$DIR/install.sh
|
||||
fi
|
||||
: "${CPPCHECK_DIR:=$(python3 -c "import cppcheck; print(cppcheck.DIR)")}"
|
||||
|
||||
# ensure checked in coverage table is up to date
|
||||
cd $DIR
|
||||
|
||||
Reference in New Issue
Block a user