diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5003f945..d24e9d4e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -35,7 +35,11 @@ jobs: test: name: ./test.sh - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ['macos-latest', 'ubuntu-latest'] timeout-minutes: 10 steps: - uses: actions/checkout@v4 diff --git a/Jenkinsfile b/Jenkinsfile index a771ff53..da692afe 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -101,6 +101,18 @@ pipeline { stage('parallel tests') { parallel { + stage('test cuatro') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + steps { + phone_steps("panda-cuatro", [ + ["build", "scons -j4"], + ["flash", "cd scripts/ && ./reflash_internal_panda.py"], + ["flash jungle", "cd board/jungle && ./flash.py --all"], + ["test", "cd tests/hitl && HW_TYPES=10 pytest -n0 --durations=0 2*.py [5-9]*.py"], + ]) + } + } + stage('test tres') { agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } steps { diff --git a/board/boards/board_declarations.h b/board/boards/board_declarations.h index f96812e9..b351b3d4 100644 --- a/board/boards/board_declarations.h +++ b/board/boards/board_declarations.h @@ -28,6 +28,7 @@ struct board { harness_configuration *harness_config; GPIO_TypeDef * const led_GPIO[3]; const uint8_t led_pin[3]; + const uint8_t led_pwm_channels[3]; // leave at 0 to disable PWM const bool has_spi; const bool has_canfd; const uint16_t fan_max_rpm; diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index d9e219c0..53b671e1 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -45,6 +45,7 @@ static void cuatro_set_bootkick(BootState state) { static void cuatro_set_amp_enabled(bool enabled){ set_gpio_output(GPIOA, 5, enabled); + set_gpio_output(GPIOB, 0, enabled); } static void cuatro_init(void) { @@ -85,11 +86,6 @@ static void cuatro_init(void) { set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); register_set_bits(&(GPIOC->OTYPER), GPIO_OTYPER_OT8); // open drain - // Initialize IR PWM and set to 0% - set_gpio_alternate(GPIOC, 9, GPIO_AF2_TIM3); - pwm_init(TIM3, 4); - tres_set_ir_power(0U); - // Clock source clock_source_init(true); @@ -133,6 +129,7 @@ board board_cuatro = { .enable_can_transceiver = cuatro_enable_can_transceiver, .led_GPIO = {GPIOC, GPIOC, GPIOC}, .led_pin = {6, 7, 9}, + .led_pwm_channels = {1, 2, 4}, .set_can_mode = tres_set_can_mode, .check_ignition = red_check_ignition, .read_voltage_mV = cuatro_read_voltage_mV, diff --git a/board/drivers/led.h b/board/drivers/led.h index f67e4f98..d927d8cf 100644 --- a/board/drivers/led.h +++ b/board/drivers/led.h @@ -3,18 +3,30 @@ #define LED_GREEN 1U #define LED_BLUE 2U +#define LED_PWM_POWER 5U + void led_set(uint8_t color, bool enabled) { if (color < 3U) { - set_gpio_output(current_board->led_GPIO[color], current_board->led_pin[color], !enabled); + if (current_board->led_pwm_channels[color] != 0U) { + pwm_set(TIM3, current_board->led_pwm_channels[color], 100U - (enabled ? LED_PWM_POWER : 0U)); + } else { + set_gpio_output(current_board->led_GPIO[color], current_board->led_pin[color], !enabled); + } } } void led_init(void) { for (uint8_t i = 0U; i<3U; i++){ set_gpio_pullup(current_board->led_GPIO[i], current_board->led_pin[i], PULL_NONE); - set_gpio_mode(current_board->led_GPIO[i], current_board->led_pin[i], MODE_OUTPUT); set_gpio_output_type(current_board->led_GPIO[i], current_board->led_pin[i], OUTPUT_TYPE_OPEN_DRAIN); + if (current_board->led_pwm_channels[i] != 0U) { + set_gpio_alternate(current_board->led_GPIO[i], current_board->led_pin[i], GPIO_AF2_TIM3); + pwm_init(TIM3, current_board->led_pwm_channels[i]); + } else { + set_gpio_mode(current_board->led_GPIO[i], current_board->led_pin[i], MODE_OUTPUT); + } + led_set(i, false); } } diff --git a/board/jungle/boards/board_declarations.h b/board/jungle/boards/board_declarations.h index e6c6601b..88427041 100644 --- a/board/jungle/boards/board_declarations.h +++ b/board/jungle/boards/board_declarations.h @@ -16,6 +16,7 @@ typedef uint16_t (*board_get_sbu_mV)(uint8_t channel, uint8_t sbu); struct board { GPIO_TypeDef * const led_GPIO[3]; const uint8_t led_pin[3]; + const uint8_t led_pwm_channels[3]; // leave at 0 to disable PWM const bool has_canfd; const bool has_sbu_sense; const uint16_t avdd_mV; diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index 2cdbea6d..3e9256da 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -73,6 +73,9 @@ void flasher_peripherals_init(void) { // SPI + DMA RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; + + // LED PWM + RCC->APB1LENR |= RCC_APB1LENR_TIM3EN; } #endif @@ -116,7 +119,7 @@ void peripherals_init(void) { // Timers RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer RCC->APB1LENR |= RCC_APB1LENR_TIM2EN; // main counter - RCC->APB1LENR |= RCC_APB1LENR_TIM3EN; // fan pwm + RCC->APB1LENR |= RCC_APB1LENR_TIM3EN; // fan + led pwm RCC->APB1LENR |= RCC_APB1LENR_TIM6EN; // interrupt timer RCC->APB1LENR |= RCC_APB1LENR_TIM7EN; // DMA trigger timer RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // tick timer diff --git a/pyproject.toml b/pyproject.toml index a32a4d7f..b7c2b74d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ classifiers = [ ] dependencies = [ "libusb1", + "opendbc @ git+https://github.com/sunnypilot/opendbc.git@c9ca0d18ca75e0448806406136b0f39adf76bf97#egg=opendbc", ] [project.optional-dependencies] @@ -28,9 +29,8 @@ dev = [ "pytest-randomly", "ruff", "mypy", - "spidev", "setuptools", - "opendbc @ git+https://github.com/sunnypilot/opendbc.git@462d16e4c6bd1d65d434adf38d3264b801632584#egg=opendbc", + "spidev; platform_system == 'Linux'", ] [build-system] diff --git a/python/__init__.py b/python/__init__.py index 091ff3bb..d6806b81 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -143,7 +143,7 @@ class Panda: HW_TYPE_UNO: 5100, HW_TYPE_DOS: 6500, HW_TYPE_TRES: 6600, - HW_TYPE_CUATRO: 6600, + HW_TYPE_CUATRO: 12500, } HARNESS_STATUS_NC = 0 diff --git a/python/socketpanda.py b/python/socketpanda.py index d6115acf..b80f1b1b 100644 --- a/python/socketpanda.py +++ b/python/socketpanda.py @@ -30,6 +30,8 @@ CANFD_FDF = 0x04 # mark CAN FD for dual use of struct canfd_frame # https://github.com/torvalds/linux/blob/47ac09b91befbb6a235ab620c32af719f8208399/include/uapi/asm-generic/socket.h#L61 SO_RXQ_OVFL = 40 +import typing +@typing.no_type_check # mypy struggles with macOS here... def create_socketcan(interface:str, recv_buffer_size:int, fd:bool) -> socket.socket: # settings mostly from https://github.com/linux-can/can-utils/blob/master/candump.c socketcan = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW) diff --git a/setup.sh b/setup.sh index 647bcd22..016b1820 100755 --- a/setup.sh +++ b/setup.sh @@ -8,6 +8,7 @@ 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 elif [[ $PLATFORM == "Linux" ]]; then @@ -23,7 +24,11 @@ fi if ! command -v uv &>/dev/null; then echo "'uv' is not installed. Installing 'uv'..." curl -LsSf https://astral.sh/uv/install.sh | sh - source $HOME/.local/bin/env || true + + # doesn't require sourcing on all platforms + set +e + source $HOME/.local/bin/env + set -e fi export UV_PROJECT_ENVIRONMENT="$DIR/.venv" diff --git a/tests/hitl/7_internal.py b/tests/hitl/7_internal.py index eb8577fb..aad62bad 100644 --- a/tests/hitl/7_internal.py +++ b/tests/hitl/7_internal.py @@ -44,7 +44,7 @@ def test_fan_cooldown(p): time.sleep(3) p.set_fan_power(0) for _ in range(5): - assert p.get_fan_rpm() <= 7000 + assert p.get_fan_rpm() <= Panda.MAX_FAN_RPMs[bytes(p.get_type())] time.sleep(0.5) def test_fan_overshoot(p):