diff --git a/SConscript b/SConscript index de770920385..430d517cf6d 100644 --- a/SConscript +++ b/SConscript @@ -61,7 +61,103 @@ def to_c_uint32(x): return "{" + 'U,'.join(map(str, nums)) + "U}" -def build_project(project_name, project, main, extra_flags): +common_driver_sources = [ + "./board/stm32h7/interrupt_handlers.c", + "./board/stm32h7/peripherals.c", + "./board/stm32h7/clock.c", + "./board/stm32h7/llfdcan.c", + "./board/stm32h7/llusb.c", + "./board/stm32h7/llspi.c", + "./board/stm32h7/lluart.c", + "./board/stm32h7/lladc.c", + "./board/drivers/gpio.c", + "./board/drivers/registers.c", + "./board/drivers/interrupts.c", + "./board/drivers/timers.c", + "./board/drivers/pwm.c", + "./board/drivers/led.c", + "./board/drivers/can_common.c", + "./board/drivers/fdcan.c", + "./board/drivers/uart.c", + "./board/drivers/spi.c", + "./board/drivers/usb.c", + "./board/can_comms.c", +] + +panda_extra_driver_sources = [ + "./board/stm32h7/board.c", + "./board/stm32h7/llfan.c", + "./board/stm32h7/lli2c.c", + "./board/stm32h7/sound.c", + "./board/boards/unused_funcs.c", + "./board/boards/red.c", + "./board/boards/tres.c", + "./board/boards/cuatro.c", + "./board/drivers/simple_watchdog.c", + "./board/drivers/bootkick.c", + "./board/drivers/clock_source.c", + "./board/drivers/fan.c", + "./board/drivers/harness.c", + "./board/drivers/fake_siren.c", + "./board/sys/power_saving.c", + "./board/main_comms.c", +] + +jungle_extra_driver_sources = [ + "./board/jungle/stm32h7/board.c", + "./board/jungle/boards/board_v2.c", + "./board/jungle/main_comms.c", +] + +body_extra_driver_sources = [ + "./board/body/stm32h7/board.c", + "./board/body/boards/board_body.c", +] + +bootstub_common_driver_sources = [ + "./board/stm32h7/interrupt_handlers.c", + "./board/stm32h7/peripherals.c", + "./board/stm32h7/clock.c", + "./board/stm32h7/llflash.c", + "./board/stm32h7/llusb.c", + "./board/stm32h7/llspi.c", + "./board/stm32h7/lladc.c", + "./board/drivers/gpio.c", + "./board/drivers/registers.c", + "./board/drivers/interrupts.c", + "./board/drivers/timers.c", + "./board/drivers/pwm.c", + "./board/drivers/led.c", + "./board/drivers/spi.c", + "./board/drivers/usb.c", +] + +panda_extra_bootstub_driver_sources = [ + "./board/stm32h7/board.c", + "./board/stm32h7/llfan.c", + "./board/stm32h7/lli2c.c", + "./board/stm32h7/sound.c", + "./board/boards/unused_funcs.c", + "./board/boards/red.c", + "./board/boards/tres.c", + "./board/boards/cuatro.c", + "./board/drivers/harness.c", + "./board/drivers/fan.c", + "./board/drivers/fake_siren.c", + "./board/drivers/clock_source.c", +] + +jungle_extra_bootstub_driver_sources = [ + "./board/jungle/stm32h7/board.c", + "./board/jungle/boards/board_v2.c", +] + +body_extra_bootstub_driver_sources = [ + "./board/body/stm32h7/board.c", + "./board/body/boards/board_body.c", +] + +def build_project(project_name, project, main, extra_flags, extra_driver_sources=None, extra_bootstub_driver_sources=None): project_dir = Dir(f'./board/obj/{project_name}/') flags = project["FLAGS"] + extra_flags + common_flags + [ @@ -100,21 +196,58 @@ def build_project(project_name, project, main, extra_flags): ) startup = env.Object(project["STARTUP_FILE"]) + libc_obj = env.Object("./board/libc.c") + crc_obj = env.Object("./board/crc.c") + provision_obj = env.Object("./board/provision.c") + critical_obj = env.Object("./board/sys/critical.c") + faults_obj = env.Object("./board/sys/faults.c") + main_globals_obj = env.Object("./board/main_globals.c") + gitversion_obj = env.Object("./board/obj/gitversion.c") + + drv_env = env.Clone() + drv_env.Append(CFLAGS="-DDRIVER_BUILD") + early_init_obj = drv_env.Object("./board/early_init.c") # Build bootstub bs_env = env.Clone() bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB") + bs_env['OBJPREFIX'] = Dir(f'{project_dir}/bs_') + bs_drv_env = bs_env.Clone() + bs_drv_env.Append(CFLAGS="-DDRIVER_BUILD") + bs_all_driver_sources = bootstub_common_driver_sources + (extra_bootstub_driver_sources or []) + bs_driver_objs = [bs_drv_env.Object(src) for src in bs_all_driver_sources] bs_elf = bs_env.Program(f"{project_dir}/bootstub.elf", [ startup, "./board/crypto/rsa.c", "./board/crypto/sha.c", + libc_obj, + crc_obj, + provision_obj, + critical_obj, + faults_obj, + gitversion_obj, + bs_env.Object("./board/bootstub_globals.c"), + bs_drv_env.Object("./board/early_init.c"), + bs_drv_env.Object("./board/flasher.c"), + ] + bs_driver_objs + [ "./board/bootstub.c", ]) bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf) # Build + sign main (aka app) + driver_sources = common_driver_sources + (extra_driver_sources or []) + driver_objs = [drv_env.Object(src) for src in driver_sources] main_elf = env.Program(f"{project_dir}/main.elf", [ startup, + libc_obj, + crc_obj, + provision_obj, + critical_obj, + faults_obj, + main_globals_obj, + gitversion_obj, + early_init_obj, + ] + driver_objs + [ main ], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf) @@ -141,6 +274,10 @@ base_project_h7 = { with open("board/obj/gitversion.h", "w") as f: version = get_version(BUILDER, BUILD_TYPE) f.write(f'extern const uint8_t gitversion[{len(version)+1}];\n') + +with open("board/obj/gitversion.c", "w") as f: + version = get_version(BUILDER, BUILD_TYPE) + f.write(f'#include \n') f.write(f'const uint8_t gitversion[{len(version)+1}] = "{version}";\n') with open("board/obj/version", "w") as f: @@ -161,16 +298,22 @@ common_flags += [f"-DHEALTH_PACKET_VERSION=0x{hh:08X}U", f"-DCAN_PACKET_VERSION_ f"-DJUNGLE_HEALTH_PACKET_VERSION=0x{jh:08X}U"] # panda fw -build_project("panda_h7", base_project_h7, "./board/main.c", []) +build_project("panda_h7", base_project_h7, "./board/main.c", [], + extra_driver_sources=panda_extra_driver_sources, + extra_bootstub_driver_sources=panda_extra_bootstub_driver_sources) # panda jungle fw flags = [ "-DPANDA_JUNGLE", ] -build_project("panda_jungle_h7", base_project_h7, "./board/jungle/main.c", flags) +build_project("panda_jungle_h7", base_project_h7, "./board/jungle/main.c", flags, + extra_driver_sources=jungle_extra_driver_sources, + extra_bootstub_driver_sources=jungle_extra_bootstub_driver_sources) # body fw -build_project("body_h7", base_project_h7, "./board/body/main.c", ["-DPANDA_BODY"]) +build_project("body_h7", base_project_h7, "./board/body/main.c", ["-DPANDA_BODY"], + extra_driver_sources=body_extra_driver_sources, + extra_bootstub_driver_sources=body_extra_bootstub_driver_sources) # test files SConscript('tests/libpanda/SConscript') diff --git a/board/boards/cuatro.c b/board/boards/cuatro.c new file mode 100644 index 00000000000..06d6422e5e1 --- /dev/null +++ b/board/boards/cuatro.c @@ -0,0 +1,139 @@ +#include "board/config.h" +#include "board/boards/unused_funcs.h" +#include "board/boards/tres.h" +#include "board/boards/cuatro.h" +#include "board/drivers/fake_siren.h" + +// ////////////////////////// // +// Cuatro (STM32H7) + Harness // +// ////////////////////////// // + +static void cuatro_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver) { + case 1U: + set_gpio_output(GPIOB, 7, !enabled); + break; + case 2U: + set_gpio_output(GPIOB, 10, !enabled); + break; + case 3U: + set_gpio_output(GPIOD, 8, !enabled); + break; + case 4U: + set_gpio_output(GPIOB, 11, !enabled); + break; + default: + break; + } +} + +static uint32_t cuatro_read_voltage_mV(void) { + return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 8)) * 11U; +} + +static uint32_t cuatro_read_current_mA(void) { + return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 3)) * 2U; +} + +static void cuatro_set_fan_enabled(bool enabled) { + set_gpio_output(GPIOD, 3, !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) { + set_gpio_output(GPIOB, 0, enabled); +} + +static void cuatro_init(void) { + common_init_gpio(); + + // open drain + set_gpio_output_type(GPIOD, 3, OUTPUT_TYPE_OPEN_DRAIN); // FAN_EN + set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); // DC_IN_EN_N + + // Power readout + set_gpio_mode(GPIOC, 5, MODE_ANALOG); + set_gpio_mode(GPIOA, 6, MODE_ANALOG); + + // CAN transceiver enables + set_gpio_pullup(GPIOB, 7, PULL_NONE); + set_gpio_mode(GPIOB, 7, MODE_OUTPUT); + set_gpio_pullup(GPIOD, 8, PULL_NONE); + set_gpio_mode(GPIOD, 8, MODE_OUTPUT); + + // FDCAN3, different pins on this package than the rest of the reds + set_gpio_pullup(GPIOD, 12, PULL_NONE); + set_gpio_alternate(GPIOD, 12, GPIO_AF5_FDCAN3); + set_gpio_pullup(GPIOD, 13, PULL_NONE); + set_gpio_alternate(GPIOD, 13, GPIO_AF5_FDCAN3); + + // C2: SOM GPIO used as input (fan control at boot) + set_gpio_mode(GPIOC, 2, MODE_INPUT); + set_gpio_pullup(GPIOC, 2, PULL_DOWN); + + // SOM bootkick + reset lines + cuatro_set_bootkick(BOOT_BOOTKICK); + + // SOM debugging UART + gpio_uart7_init(); + uart_init(&uart_ring_som_debug, 115200); + + // fan setup + set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); + register_set_bits(&(GPIOC->OTYPER), GPIO_OTYPER_OT8); // open drain + + // Clock source + clock_source_init(true); + + // Sound codec + cuatro_set_amp_enabled(false); + set_gpio_alternate(GPIOA, 2, GPIO_AF8_SAI4); // SAI4_SCK_B + set_gpio_alternate(GPIOC, 0, GPIO_AF8_SAI4); // SAI4_FS_B + set_gpio_alternate(GPIOD, 11, GPIO_AF10_SAI4); // SAI4_SD_A + set_gpio_alternate(GPIOE, 3, GPIO_AF8_SAI4); // SAI4_SD_B + set_gpio_alternate(GPIOE, 4, GPIO_AF3_DFSDM1); // DFSDM1_DATIN3 + set_gpio_alternate(GPIOE, 9, GPIO_AF3_DFSDM1); // DFSDM1_CKOUT + set_gpio_alternate(GPIOE, 6, GPIO_AF10_SAI4); // SAI4_MCLK_B + sound_init(); +} + +static harness_configuration cuatro_harness_config = { + .GPIO_SBU1 = GPIOC, + .GPIO_SBU2 = GPIOA, + .GPIO_relay_SBU1 = GPIOA, + .GPIO_relay_SBU2 = GPIOA, + .pin_SBU1 = 4, + .pin_SBU2 = 1, + .pin_relay_SBU1 = 9, + .pin_relay_SBU2 = 3, + .adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4), + .adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17) +}; + +board board_cuatro = { + .harness_config = &cuatro_harness_config, + .has_spi = true, + .has_fan = true, + .avdd_mV = 1800U, + .fan_enable_cooldown_time = 3U, + .init = cuatro_init, + .init_bootloader = unused_init_bootloader, + .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, + .read_voltage_mV = cuatro_read_voltage_mV, + .read_current_mA = cuatro_read_current_mA, + .set_fan_enabled = cuatro_set_fan_enabled, + .set_ir_power = unused_set_ir_power, + .set_siren = fake_siren_set, + .set_bootkick = cuatro_set_bootkick, + .read_som_gpio = tres_read_som_gpio, + .set_amp_enabled = cuatro_set_amp_enabled +}; diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index 61e5ba9c112..490d523bb43 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -1,137 +1,3 @@ #pragma once #include "board_declarations.h" - -// ////////////////////////// // -// Cuatro (STM32H7) + Harness // -// ////////////////////////// // - -static void cuatro_enable_can_transceiver(uint8_t transceiver, bool enabled) { - switch (transceiver) { - case 1U: - set_gpio_output(GPIOB, 7, !enabled); - break; - case 2U: - set_gpio_output(GPIOB, 10, !enabled); - break; - case 3U: - set_gpio_output(GPIOD, 8, !enabled); - break; - case 4U: - set_gpio_output(GPIOB, 11, !enabled); - break; - default: - break; - } -} - -static uint32_t cuatro_read_voltage_mV(void) { - return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 8)) * 11U; -} - -static uint32_t cuatro_read_current_mA(void) { - return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 3)) * 2U; -} - -static void cuatro_set_fan_enabled(bool enabled) { - set_gpio_output(GPIOD, 3, !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) { - set_gpio_output(GPIOB, 0, enabled); -} - -static void cuatro_init(void) { - common_init_gpio(); - - // open drain - set_gpio_output_type(GPIOD, 3, OUTPUT_TYPE_OPEN_DRAIN); // FAN_EN - set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); // DC_IN_EN_N - - // Power readout - set_gpio_mode(GPIOC, 5, MODE_ANALOG); - set_gpio_mode(GPIOA, 6, MODE_ANALOG); - - // CAN transceiver enables - set_gpio_pullup(GPIOB, 7, PULL_NONE); - set_gpio_mode(GPIOB, 7, MODE_OUTPUT); - set_gpio_pullup(GPIOD, 8, PULL_NONE); - set_gpio_mode(GPIOD, 8, MODE_OUTPUT); - - // FDCAN3, different pins on this package than the rest of the reds - set_gpio_pullup(GPIOD, 12, PULL_NONE); - set_gpio_alternate(GPIOD, 12, GPIO_AF5_FDCAN3); - set_gpio_pullup(GPIOD, 13, PULL_NONE); - set_gpio_alternate(GPIOD, 13, GPIO_AF5_FDCAN3); - - // C2: SOM GPIO used as input (fan control at boot) - set_gpio_mode(GPIOC, 2, MODE_INPUT); - set_gpio_pullup(GPIOC, 2, PULL_DOWN); - - // SOM bootkick + reset lines - cuatro_set_bootkick(BOOT_BOOTKICK); - - // SOM debugging UART - gpio_uart7_init(); - uart_init(&uart_ring_som_debug, 115200); - - // fan setup - set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); - register_set_bits(&(GPIOC->OTYPER), GPIO_OTYPER_OT8); // open drain - - // Clock source - clock_source_init(true); - - // Sound codec - cuatro_set_amp_enabled(false); - set_gpio_alternate(GPIOA, 2, GPIO_AF8_SAI4); // SAI4_SCK_B - set_gpio_alternate(GPIOC, 0, GPIO_AF8_SAI4); // SAI4_FS_B - set_gpio_alternate(GPIOD, 11, GPIO_AF10_SAI4); // SAI4_SD_A - set_gpio_alternate(GPIOE, 3, GPIO_AF8_SAI4); // SAI4_SD_B - set_gpio_alternate(GPIOE, 4, GPIO_AF3_DFSDM1); // DFSDM1_DATIN3 - set_gpio_alternate(GPIOE, 9, GPIO_AF3_DFSDM1); // DFSDM1_CKOUT - set_gpio_alternate(GPIOE, 6, GPIO_AF10_SAI4); // SAI4_MCLK_B - sound_init(); -} - -static harness_configuration cuatro_harness_config = { - .GPIO_SBU1 = GPIOC, - .GPIO_SBU2 = GPIOA, - .GPIO_relay_SBU1 = GPIOA, - .GPIO_relay_SBU2 = GPIOA, - .pin_SBU1 = 4, - .pin_SBU2 = 1, - .pin_relay_SBU1 = 9, - .pin_relay_SBU2 = 3, - .adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4), - .adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17) -}; - -board board_cuatro = { - .harness_config = &cuatro_harness_config, - .has_spi = true, - .has_fan = true, - .avdd_mV = 1800U, - .fan_enable_cooldown_time = 3U, - .init = cuatro_init, - .init_bootloader = unused_init_bootloader, - .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, - .read_voltage_mV = cuatro_read_voltage_mV, - .read_current_mA = cuatro_read_current_mA, - .set_fan_enabled = cuatro_set_fan_enabled, - .set_ir_power = unused_set_ir_power, - .set_siren = fake_siren_set, - .set_bootkick = cuatro_set_bootkick, - .read_som_gpio = tres_read_som_gpio, - .set_amp_enabled = cuatro_set_amp_enabled -}; diff --git a/board/boards/red.c b/board/boards/red.c new file mode 100644 index 00000000000..cb976b1d1e3 --- /dev/null +++ b/board/boards/red.c @@ -0,0 +1,134 @@ +#include "board/config.h" +#include "board/boards/unused_funcs.h" +#include "board/boards/red.h" + +// ///////////////////////////// // +// Red Panda (STM32H7) + Harness // +// ///////////////////////////// // + +static void red_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver) { + case 1U: + set_gpio_output(GPIOG, 11, !enabled); + break; + case 2U: + set_gpio_output(GPIOB, 3, !enabled); + break; + case 3U: + set_gpio_output(GPIOD, 7, !enabled); + break; + case 4U: + set_gpio_output(GPIOB, 4, !enabled); + break; + default: + break; + } +} + +static void red_set_can_mode(uint8_t mode) { + red_enable_can_transceiver(2U, false); + red_enable_can_transceiver(4U, false); + switch (mode) { + case CAN_MODE_NORMAL: + case CAN_MODE_OBD_CAN2: + if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) { + // B12,B13: disable normal mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_mode(GPIOB, 12, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_mode(GPIOB, 13, MODE_ANALOG); + + // B5,B6: FDCAN2 mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); + red_enable_can_transceiver(2U, true); + } else { + // B5,B6: disable normal mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_mode(GPIOB, 5, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_mode(GPIOB, 6, MODE_ANALOG); + // B12,B13: FDCAN2 mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); + red_enable_can_transceiver(4U, true); + } + break; + default: + break; + } +} + +uint32_t red_read_voltage_mV(void){ + return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 2)) * 11U; +} + +static void red_init(void) { + common_init_gpio(); + + // G11,B3,D7,B4: transceiver enable + set_gpio_pullup(GPIOG, 11, PULL_NONE); + set_gpio_mode(GPIOG, 11, MODE_OUTPUT); + + set_gpio_pullup(GPIOB, 3, PULL_NONE); + set_gpio_mode(GPIOB, 3, MODE_OUTPUT); + + set_gpio_pullup(GPIOD, 7, PULL_NONE); + set_gpio_mode(GPIOD, 7, MODE_OUTPUT); + + set_gpio_pullup(GPIOB, 4, PULL_NONE); + set_gpio_mode(GPIOB, 4, MODE_OUTPUT); + + //B1: 5VOUT_S + set_gpio_pullup(GPIOB, 1, PULL_NONE); + set_gpio_mode(GPIOB, 1, MODE_ANALOG); + + // B14: usb load switch, enabled by pull resistor on board, obsolete for red panda + set_gpio_output_type(GPIOB, 14, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_pullup(GPIOB, 14, PULL_UP); + set_gpio_mode(GPIOB, 14, MODE_OUTPUT); + set_gpio_output(GPIOB, 14, 1); +} + +static harness_configuration red_harness_config = { + .GPIO_SBU1 = GPIOC, + .GPIO_SBU2 = GPIOA, + .GPIO_relay_SBU1 = GPIOC, + .GPIO_relay_SBU2 = GPIOC, + .pin_SBU1 = 4, + .pin_SBU2 = 1, + .pin_relay_SBU1 = 10, + .pin_relay_SBU2 = 11, + .adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4), + .adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17) +}; + +board board_red = { + .set_bootkick = unused_set_bootkick, + .harness_config = &red_harness_config, + .has_spi = false, + .has_fan = false, + .avdd_mV = 3300U, + .fan_enable_cooldown_time = 0U, + .init = red_init, + .init_bootloader = unused_init_bootloader, + .enable_can_transceiver = red_enable_can_transceiver, + .led_GPIO = {GPIOE, GPIOE, GPIOE}, + .led_pin = {4, 3, 2}, + .set_can_mode = red_set_can_mode, + .read_voltage_mV = red_read_voltage_mV, + .read_current_mA = unused_read_current, + .set_fan_enabled = unused_set_fan_enabled, + .set_ir_power = unused_set_ir_power, + .set_siren = unused_set_siren, + .read_som_gpio = unused_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled +}; diff --git a/board/boards/red.h b/board/boards/red.h index 761c2799ec8..dc27e17cae4 100644 --- a/board/boards/red.h +++ b/board/boards/red.h @@ -2,133 +2,4 @@ #include "board_declarations.h" -// ///////////////////////////// // -// Red Panda (STM32H7) + Harness // -// ///////////////////////////// // - -static void red_enable_can_transceiver(uint8_t transceiver, bool enabled) { - switch (transceiver) { - case 1U: - set_gpio_output(GPIOG, 11, !enabled); - break; - case 2U: - set_gpio_output(GPIOB, 3, !enabled); - break; - case 3U: - set_gpio_output(GPIOD, 7, !enabled); - break; - case 4U: - set_gpio_output(GPIOB, 4, !enabled); - break; - default: - break; - } -} - -static void red_set_can_mode(uint8_t mode) { - red_enable_can_transceiver(2U, false); - red_enable_can_transceiver(4U, false); - switch (mode) { - case CAN_MODE_NORMAL: - case CAN_MODE_OBD_CAN2: - if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) { - // B12,B13: disable normal mode - set_gpio_pullup(GPIOB, 12, PULL_NONE); - set_gpio_mode(GPIOB, 12, MODE_ANALOG); - - set_gpio_pullup(GPIOB, 13, PULL_NONE); - set_gpio_mode(GPIOB, 13, MODE_ANALOG); - - // B5,B6: FDCAN2 mode - set_gpio_pullup(GPIOB, 5, PULL_NONE); - set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); - - set_gpio_pullup(GPIOB, 6, PULL_NONE); - set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); - red_enable_can_transceiver(2U, true); - } else { - // B5,B6: disable normal mode - set_gpio_pullup(GPIOB, 5, PULL_NONE); - set_gpio_mode(GPIOB, 5, MODE_ANALOG); - - set_gpio_pullup(GPIOB, 6, PULL_NONE); - set_gpio_mode(GPIOB, 6, MODE_ANALOG); - // B12,B13: FDCAN2 mode - set_gpio_pullup(GPIOB, 12, PULL_NONE); - set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2); - - set_gpio_pullup(GPIOB, 13, PULL_NONE); - set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); - red_enable_can_transceiver(4U, true); - } - break; - default: - break; - } -} - -static uint32_t red_read_voltage_mV(void){ - return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 2)) * 11U; -} - -static void red_init(void) { - common_init_gpio(); - - // G11,B3,D7,B4: transceiver enable - set_gpio_pullup(GPIOG, 11, PULL_NONE); - set_gpio_mode(GPIOG, 11, MODE_OUTPUT); - - set_gpio_pullup(GPIOB, 3, PULL_NONE); - set_gpio_mode(GPIOB, 3, MODE_OUTPUT); - - set_gpio_pullup(GPIOD, 7, PULL_NONE); - set_gpio_mode(GPIOD, 7, MODE_OUTPUT); - - set_gpio_pullup(GPIOB, 4, PULL_NONE); - set_gpio_mode(GPIOB, 4, MODE_OUTPUT); - - //B1: 5VOUT_S - set_gpio_pullup(GPIOB, 1, PULL_NONE); - set_gpio_mode(GPIOB, 1, MODE_ANALOG); - - // B14: usb load switch, enabled by pull resistor on board, obsolete for red panda - set_gpio_output_type(GPIOB, 14, OUTPUT_TYPE_OPEN_DRAIN); - set_gpio_pullup(GPIOB, 14, PULL_UP); - set_gpio_mode(GPIOB, 14, MODE_OUTPUT); - set_gpio_output(GPIOB, 14, 1); -} - -static harness_configuration red_harness_config = { - .GPIO_SBU1 = GPIOC, - .GPIO_SBU2 = GPIOA, - .GPIO_relay_SBU1 = GPIOC, - .GPIO_relay_SBU2 = GPIOC, - .pin_SBU1 = 4, - .pin_SBU2 = 1, - .pin_relay_SBU1 = 10, - .pin_relay_SBU2 = 11, - .adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4), - .adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17) -}; - -board board_red = { - .set_bootkick = unused_set_bootkick, - .harness_config = &red_harness_config, - .has_spi = false, - .has_fan = false, - .avdd_mV = 3300U, - .fan_enable_cooldown_time = 0U, - .init = red_init, - .init_bootloader = unused_init_bootloader, - .enable_can_transceiver = red_enable_can_transceiver, - .led_GPIO = {GPIOE, GPIOE, GPIOE}, - .led_pin = {4, 3, 2}, - .set_can_mode = red_set_can_mode, - .read_voltage_mV = red_read_voltage_mV, - .read_current_mA = unused_read_current, - .set_fan_enabled = unused_set_fan_enabled, - .set_ir_power = unused_set_ir_power, - .set_siren = unused_set_siren, - .read_som_gpio = unused_read_som_gpio, - .set_amp_enabled = unused_set_amp_enabled -}; +uint32_t red_read_voltage_mV(void); diff --git a/board/boards/tres.c b/board/boards/tres.c new file mode 100644 index 00000000000..59b5a2628aa --- /dev/null +++ b/board/boards/tres.c @@ -0,0 +1,177 @@ +#include "board/config.h" +#include "board/boards/unused_funcs.h" +#include "board/boards/red.h" +#include "board/boards/tres.h" +#include "board/drivers/fake_siren.h" + +// /////////////////////////// +// Tres (STM32H7) + Harness // +// /////////////////////////// + +static bool tres_ir_enabled; +static bool tres_fan_enabled; +static void tres_update_fan_ir_power(void) { + set_gpio_output(GPIOD, 3, tres_ir_enabled || tres_fan_enabled); +} + +static void tres_set_ir_power(uint8_t percentage){ + tres_ir_enabled = (percentage > 0U); + tres_update_fan_ir_power(); + pwm_set(TIM3, 4, percentage); +} + +static void tres_set_bootkick(BootState state) { + set_gpio_output(GPIOA, 0, state != BOOT_BOOTKICK); + set_gpio_output(GPIOC, 12, state != BOOT_RESET); +} + +static void tres_set_fan_enabled(bool enabled) { + // NOTE: fan controller reset doesn't work on a tres if IR is enabled + tres_fan_enabled = enabled; + tres_update_fan_ir_power(); +} + +static void tres_enable_can_transceiver(uint8_t transceiver, bool enabled) { + static bool can0_enabled = false; + static bool can2_enabled = false; + + switch (transceiver) { + case 1U: + can0_enabled = enabled; + break; + case 2U: + set_gpio_output(GPIOB, 10, !enabled); + break; + case 3U: + can2_enabled = enabled; + break; + case 4U: + set_gpio_output(GPIOB, 11, !enabled); + break; + default: + break; + } + + // CAN0 and 2 are tied, so enable both if either is enabled + set_gpio_output(GPIOG, 11, !(can0_enabled || can2_enabled)); + set_gpio_output(GPIOD, 7, !(can0_enabled || can2_enabled)); +} + +void tres_set_can_mode(uint8_t mode) { + current_board->enable_can_transceiver(2U, false); + current_board->enable_can_transceiver(4U, false); + switch (mode) { + case CAN_MODE_NORMAL: + case CAN_MODE_OBD_CAN2: + if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) { + // B12,B13: disable normal mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_mode(GPIOB, 12, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_mode(GPIOB, 13, MODE_ANALOG); + + // B5,B6: FDCAN2 mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); + current_board->enable_can_transceiver(2U, true); + } else { + // B5,B6: disable normal mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_mode(GPIOB, 5, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_mode(GPIOB, 6, MODE_ANALOG); + // B12,B13: FDCAN2 mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); + current_board->enable_can_transceiver(4U, true); + } + break; + default: + break; + } +} + +bool tres_read_som_gpio (void) { + return (get_gpio_input(GPIOC, 2) != 0); +} + +static void tres_init(void) { + // Enable USB 3.3V LDO for USB block + register_set_bits(&(PWR->CR3), PWR_CR3_USBREGEN); + register_set_bits(&(PWR->CR3), PWR_CR3_USB33DEN); + while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0U) {} + + common_init_gpio(); + + // C2: SOM GPIO used as input (fan control at boot) + set_gpio_mode(GPIOC, 2, MODE_INPUT); + set_gpio_pullup(GPIOC, 2, PULL_DOWN); + + // SOM bootkick + reset lines + // WARNING: make sure output state is set before configuring as output + tres_set_bootkick(BOOT_BOOTKICK); + set_gpio_mode(GPIOC, 12, MODE_OUTPUT); + + // SOM debugging UART + gpio_uart7_init(); + uart_init(&uart_ring_som_debug, 115200); + + // fan setup + set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); + + // Initialize IR PWM and set to 0% + set_gpio_alternate(GPIOC, 9, GPIO_AF2_TIM3); + pwm_init(TIM3, 4); + tres_set_ir_power(0U); + + // Fake siren + set_gpio_alternate(GPIOC, 10, GPIO_AF4_I2C5); + set_gpio_alternate(GPIOC, 11, GPIO_AF4_I2C5); + register_set_bits(&(GPIOC->OTYPER), GPIO_OTYPER_OT10 | GPIO_OTYPER_OT11); // open drain + + // Clock source + clock_source_init(false); +} + +static harness_configuration tres_harness_config = { + .GPIO_SBU1 = GPIOC, + .GPIO_SBU2 = GPIOA, + .GPIO_relay_SBU1 = GPIOA, + .GPIO_relay_SBU2 = GPIOA, + .pin_SBU1 = 4, + .pin_SBU2 = 1, + .pin_relay_SBU1 = 8, + .pin_relay_SBU2 = 3, + .adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4), + .adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17) +}; + +board board_tres = { + .harness_config = &tres_harness_config, + .has_spi = true, + .has_fan = true, + .avdd_mV = 1800U, + .fan_enable_cooldown_time = 3U, + .init = tres_init, + .init_bootloader = unused_init_bootloader, + .enable_can_transceiver = tres_enable_can_transceiver, + .led_GPIO = {GPIOE, GPIOE, GPIOE}, + .led_pin = {4, 3, 2}, + .set_can_mode = tres_set_can_mode, + .read_voltage_mV = red_read_voltage_mV, + .read_current_mA = unused_read_current, + .set_fan_enabled = tres_set_fan_enabled, + .set_ir_power = tres_set_ir_power, + .set_siren = fake_i2c_siren_set, + .set_bootkick = tres_set_bootkick, + .read_som_gpio = tres_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled +}; diff --git a/board/boards/tres.h b/board/boards/tres.h index 653df969925..1135e2be70b 100644 --- a/board/boards/tres.h +++ b/board/boards/tres.h @@ -2,174 +2,5 @@ #include "board_declarations.h" -// /////////////////////////// -// Tres (STM32H7) + Harness // -// /////////////////////////// - -static bool tres_ir_enabled; -static bool tres_fan_enabled; -static void tres_update_fan_ir_power(void) { - set_gpio_output(GPIOD, 3, tres_ir_enabled || tres_fan_enabled); -} - -static void tres_set_ir_power(uint8_t percentage){ - tres_ir_enabled = (percentage > 0U); - tres_update_fan_ir_power(); - pwm_set(TIM3, 4, percentage); -} - -static void tres_set_bootkick(BootState state) { - set_gpio_output(GPIOA, 0, state != BOOT_BOOTKICK); - set_gpio_output(GPIOC, 12, state != BOOT_RESET); -} - -static void tres_set_fan_enabled(bool enabled) { - // NOTE: fan controller reset doesn't work on a tres if IR is enabled - tres_fan_enabled = enabled; - tres_update_fan_ir_power(); -} - -static void tres_enable_can_transceiver(uint8_t transceiver, bool enabled) { - static bool can0_enabled = false; - static bool can2_enabled = false; - - switch (transceiver) { - case 1U: - can0_enabled = enabled; - break; - case 2U: - set_gpio_output(GPIOB, 10, !enabled); - break; - case 3U: - can2_enabled = enabled; - break; - case 4U: - set_gpio_output(GPIOB, 11, !enabled); - break; - default: - break; - } - - // CAN0 and 2 are tied, so enable both if either is enabled - set_gpio_output(GPIOG, 11, !(can0_enabled || can2_enabled)); - set_gpio_output(GPIOD, 7, !(can0_enabled || can2_enabled)); -} - -static void tres_set_can_mode(uint8_t mode) { - current_board->enable_can_transceiver(2U, false); - current_board->enable_can_transceiver(4U, false); - switch (mode) { - case CAN_MODE_NORMAL: - case CAN_MODE_OBD_CAN2: - if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) { - // B12,B13: disable normal mode - set_gpio_pullup(GPIOB, 12, PULL_NONE); - set_gpio_mode(GPIOB, 12, MODE_ANALOG); - - set_gpio_pullup(GPIOB, 13, PULL_NONE); - set_gpio_mode(GPIOB, 13, MODE_ANALOG); - - // B5,B6: FDCAN2 mode - set_gpio_pullup(GPIOB, 5, PULL_NONE); - set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); - - set_gpio_pullup(GPIOB, 6, PULL_NONE); - set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); - current_board->enable_can_transceiver(2U, true); - } else { - // B5,B6: disable normal mode - set_gpio_pullup(GPIOB, 5, PULL_NONE); - set_gpio_mode(GPIOB, 5, MODE_ANALOG); - - set_gpio_pullup(GPIOB, 6, PULL_NONE); - set_gpio_mode(GPIOB, 6, MODE_ANALOG); - // B12,B13: FDCAN2 mode - set_gpio_pullup(GPIOB, 12, PULL_NONE); - set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2); - - set_gpio_pullup(GPIOB, 13, PULL_NONE); - set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); - current_board->enable_can_transceiver(4U, true); - } - break; - default: - break; - } -} - -static bool tres_read_som_gpio (void) { - return (get_gpio_input(GPIOC, 2) != 0); -} - -static void tres_init(void) { - // Enable USB 3.3V LDO for USB block - register_set_bits(&(PWR->CR3), PWR_CR3_USBREGEN); - register_set_bits(&(PWR->CR3), PWR_CR3_USB33DEN); - while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0U); - - common_init_gpio(); - - // C2: SOM GPIO used as input (fan control at boot) - set_gpio_mode(GPIOC, 2, MODE_INPUT); - set_gpio_pullup(GPIOC, 2, PULL_DOWN); - - // SOM bootkick + reset lines - // WARNING: make sure output state is set before configuring as output - tres_set_bootkick(BOOT_BOOTKICK); - set_gpio_mode(GPIOC, 12, MODE_OUTPUT); - - // SOM debugging UART - gpio_uart7_init(); - uart_init(&uart_ring_som_debug, 115200); - - // fan setup - set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); - - // Initialize IR PWM and set to 0% - set_gpio_alternate(GPIOC, 9, GPIO_AF2_TIM3); - pwm_init(TIM3, 4); - tres_set_ir_power(0U); - - // Fake siren - set_gpio_alternate(GPIOC, 10, GPIO_AF4_I2C5); - set_gpio_alternate(GPIOC, 11, GPIO_AF4_I2C5); - register_set_bits(&(GPIOC->OTYPER), GPIO_OTYPER_OT10 | GPIO_OTYPER_OT11); // open drain - - // Clock source - clock_source_init(false); -} - -static harness_configuration tres_harness_config = { - .GPIO_SBU1 = GPIOC, - .GPIO_SBU2 = GPIOA, - .GPIO_relay_SBU1 = GPIOA, - .GPIO_relay_SBU2 = GPIOA, - .pin_SBU1 = 4, - .pin_SBU2 = 1, - .pin_relay_SBU1 = 8, - .pin_relay_SBU2 = 3, - .adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 4), - .adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 17) -}; - -board board_tres = { - .harness_config = &tres_harness_config, - .has_spi = true, - .has_fan = true, - .avdd_mV = 1800U, - .fan_enable_cooldown_time = 3U, - .init = tres_init, - .init_bootloader = unused_init_bootloader, - .enable_can_transceiver = tres_enable_can_transceiver, - .led_GPIO = {GPIOE, GPIOE, GPIOE}, - .led_pin = {4, 3, 2}, - .set_can_mode = tres_set_can_mode, - .read_voltage_mV = red_read_voltage_mV, - .read_current_mA = unused_read_current, - .set_fan_enabled = tres_set_fan_enabled, - .set_ir_power = tres_set_ir_power, - .set_siren = fake_i2c_siren_set, - .set_bootkick = tres_set_bootkick, - .read_som_gpio = tres_read_som_gpio, - .set_amp_enabled = unused_set_amp_enabled -}; +void tres_set_can_mode(uint8_t mode); +bool tres_read_som_gpio(void); diff --git a/board/boards/unused_funcs.c b/board/boards/unused_funcs.c new file mode 100644 index 00000000000..cfe7a73153b --- /dev/null +++ b/board/boards/unused_funcs.c @@ -0,0 +1,33 @@ +#include "board/config.h" +#include "board/boards/unused_funcs.h" + +void unused_init_bootloader(void) { +} + +void unused_set_ir_power(uint8_t percentage) { + UNUSED(percentage); +} + +void unused_set_fan_enabled(bool enabled) { + UNUSED(enabled); +} + +void unused_set_siren(bool enabled) { + UNUSED(enabled); +} + +uint32_t unused_read_current(void) { + return 0U; +} + +void unused_set_bootkick(BootState state) { + UNUSED(state); +} + +bool unused_read_som_gpio(void) { + return false; +} + +void unused_set_amp_enabled(bool enabled) { + UNUSED(enabled); +} diff --git a/board/boards/unused_funcs.h b/board/boards/unused_funcs.h index 588cf63654d..87fb67480de 100644 --- a/board/boards/unused_funcs.h +++ b/board/boards/unused_funcs.h @@ -1,32 +1,12 @@ #pragma once -void unused_init_bootloader(void) { -} - -void unused_set_ir_power(uint8_t percentage) { - UNUSED(percentage); -} - -void unused_set_fan_enabled(bool enabled) { - UNUSED(enabled); -} - -void unused_set_siren(bool enabled) { - UNUSED(enabled); -} - -uint32_t unused_read_current(void) { - return 0U; -} - -void unused_set_bootkick(BootState state) { - UNUSED(state); -} - -bool unused_read_som_gpio(void) { - return false; -} - -void unused_set_amp_enabled(bool enabled) { - UNUSED(enabled); -} +#include "board_declarations.h" + +void unused_init_bootloader(void); +void unused_set_ir_power(uint8_t percentage); +void unused_set_fan_enabled(bool enabled); +void unused_set_siren(bool enabled); +uint32_t unused_read_current(void); +void unused_set_bootkick(BootState state); +bool unused_read_som_gpio(void); +void unused_set_amp_enabled(bool enabled); diff --git a/board/body/boards/board_body.c b/board/body/boards/board_body.c new file mode 100644 index 00000000000..44faf020048 --- /dev/null +++ b/board/body/boards/board_body.c @@ -0,0 +1,47 @@ +#include "board/config.h" +#include "board/body/boards/board_body.h" + +void board_body_init(void) { + // Initialize CAN pins + set_gpio_pullup(CAN_RX_PORT, CAN_RX_PIN, PULL_NONE); + set_gpio_alternate(CAN_RX_PORT, CAN_RX_PIN, GPIO_AF9_FDCAN1); + set_gpio_pullup(CAN_TX_PORT, CAN_TX_PIN, PULL_NONE); + set_gpio_alternate(CAN_TX_PORT, CAN_TX_PIN, GPIO_AF9_FDCAN1); + + // Initialize button input (PC15) + set_gpio_mode(IGNITION_SW_PORT, IGNITION_SW_PIN, MODE_INPUT); + SYSCFG->EXTICR[3] &= ~(SYSCFG_EXTICR4_EXTI15); + SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI15_PC; + EXTI->PR1 = (1U << 15); + EXTI->RTSR1 |= (1U << 15); + EXTI->FTSR1 |= (1U << 15); + EXTI->IMR1 |= (1U << 15); + + // Initialize barrel jack detection input (PC13) + set_gpio_pullup(CHARGING_DETECT_PORT, CHARGING_DETECT_PIN, PULL_UP); + set_gpio_mode(CHARGING_DETECT_PORT, CHARGING_DETECT_PIN, MODE_INPUT); + SYSCFG->EXTICR[3] &= ~(SYSCFG_EXTICR4_EXTI13); + SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI13_PC; + EXTI->PR1 = (1U << 13); + EXTI->RTSR1 |= (1U << 13); + EXTI->FTSR1 |= (1U << 13); + EXTI->IMR1 |= (1U << 13); + + // Initialize and turn on mici power + set_gpio_mode(OBDC_POWER_ON_PORT, OBDC_POWER_ON_PIN, MODE_OUTPUT); + set_gpio_output(OBDC_POWER_ON_PORT, OBDC_POWER_ON_PIN, 1); + + // Initialize and turn off gpu power + set_gpio_mode(GPU_POWER_ON_PORT, GPU_POWER_ON_PIN, MODE_OUTPUT); + set_gpio_output(GPU_POWER_ON_PORT, GPU_POWER_ON_PIN, 0); + + // Initialize and turn off ignition output + set_gpio_mode(OBDC_IGNITION_ON_PORT, OBDC_IGNITION_ON_PIN, MODE_OUTPUT); + set_gpio_output(OBDC_IGNITION_ON_PORT, OBDC_IGNITION_ON_PIN, 0); +} + +board board_body = { + .led_GPIO = {GPIOA, GPIOA, GPIOA}, + .led_pin = {10, 10, 10}, + .init = board_body_init, +}; diff --git a/board/body/boards/board_body.h b/board/body/boards/board_body.h index f81cebd9712..490d523bb43 100644 --- a/board/body/boards/board_body.h +++ b/board/body/boards/board_body.h @@ -1,46 +1,3 @@ -#include "board/body/boards/board_declarations.h" +#pragma once -void board_body_init(void) { - // Initialize CAN pins - set_gpio_pullup(CAN_RX_PORT, CAN_RX_PIN, PULL_NONE); - set_gpio_alternate(CAN_RX_PORT, CAN_RX_PIN, GPIO_AF9_FDCAN1); - set_gpio_pullup(CAN_TX_PORT, CAN_TX_PIN, PULL_NONE); - set_gpio_alternate(CAN_TX_PORT, CAN_TX_PIN, GPIO_AF9_FDCAN1); - - // Initialize button input (PC15) - set_gpio_mode(IGNITION_SW_PORT, IGNITION_SW_PIN, MODE_INPUT); - SYSCFG->EXTICR[3] &= ~(SYSCFG_EXTICR4_EXTI15); - SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI15_PC; - EXTI->PR1 = (1U << 15); - EXTI->RTSR1 |= (1U << 15); - EXTI->FTSR1 |= (1U << 15); - EXTI->IMR1 |= (1U << 15); - - // Initialize barrel jack detection input (PC13) - set_gpio_pullup(CHARGING_DETECT_PORT, CHARGING_DETECT_PIN, PULL_UP); - set_gpio_mode(CHARGING_DETECT_PORT, CHARGING_DETECT_PIN, MODE_INPUT); - SYSCFG->EXTICR[3] &= ~(SYSCFG_EXTICR4_EXTI13); - SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI13_PC; - EXTI->PR1 = (1U << 13); - EXTI->RTSR1 |= (1U << 13); - EXTI->FTSR1 |= (1U << 13); - EXTI->IMR1 |= (1U << 13); - - // Initialize and turn on mici power - set_gpio_mode(OBDC_POWER_ON_PORT, OBDC_POWER_ON_PIN, MODE_OUTPUT); - set_gpio_output(OBDC_POWER_ON_PORT, OBDC_POWER_ON_PIN, 1); - - // Initialize and turn off gpu power - set_gpio_mode(GPU_POWER_ON_PORT, GPU_POWER_ON_PIN, MODE_OUTPUT); - set_gpio_output(GPU_POWER_ON_PORT, GPU_POWER_ON_PIN, 0); - - // Initialize and turn off ignition output - set_gpio_mode(OBDC_IGNITION_ON_PORT, OBDC_IGNITION_ON_PIN, MODE_OUTPUT); - set_gpio_output(OBDC_IGNITION_ON_PORT, OBDC_IGNITION_ON_PIN, 0); -} - -board board_body = { - .led_GPIO = {GPIOA, GPIOA, GPIOA}, - .led_pin = {10, 10, 10}, - .init = board_body_init, -}; \ No newline at end of file +#include "board_declarations.h" diff --git a/board/body/boards/board_declarations.h b/board/body/boards/board_declarations.h index 269e6faad8b..ac540511612 100644 --- a/board/body/boards/board_declarations.h +++ b/board/body/boards/board_declarations.h @@ -18,6 +18,8 @@ struct board { const bool has_spi; }; +extern struct board board_body; + // ******************* Definitions ******************** #define HW_TYPE_BODY 0xB1U diff --git a/board/body/can.h b/board/body/can.h index 50dea369ab4..cabe2efe944 100644 --- a/board/body/can.h +++ b/board/body/can.h @@ -6,7 +6,7 @@ #include "board/can.h" #include "board/health.h" #include "board/body/boards/board_declarations.h" -#include "board/drivers/drivers.h" +#include "board/drivers/can_common.h" #include "opendbc/safety/declarations.h" #include "board/body/bldc/bldc.h" diff --git a/board/body/main.c b/board/body/main.c index 726d480770d..e82a115b386 100644 --- a/board/body/main.c +++ b/board/body/main.c @@ -2,6 +2,7 @@ #include #include "board/config.h" +#include "board/body/stm32h7/board.h" #include "board/drivers/led.h" #include "board/drivers/pwm.h" #include "board/drivers/usb.h" diff --git a/board/body/stm32h7/board.c b/board/body/stm32h7/board.c new file mode 100644 index 00000000000..51edfaf677f --- /dev/null +++ b/board/body/stm32h7/board.c @@ -0,0 +1,5 @@ +#include "board/config.h" + +void detect_board_type(void) { + // Board type set explicitly in main() +} diff --git a/board/body/stm32h7/board.h b/board/body/stm32h7/board.h index c063e4ddbb5..baeac3b9dc0 100644 --- a/board/body/stm32h7/board.h +++ b/board/body/stm32h7/board.h @@ -1,9 +1,6 @@ +#pragma once + #include "board/body/boards/board_declarations.h" #include "board/body/boards/board_body.h" -extern board *current_board; -extern uint8_t hw_type; - -void detect_board_type(void) { - // Board type set explicitly in main() -} +void detect_board_type(void); diff --git a/board/bootstub.c b/board/bootstub.c index c09cb9fe1b7..6a8b2480657 100644 --- a/board/bootstub.c +++ b/board/bootstub.c @@ -4,6 +4,14 @@ // ********************* Includes ********************* #include "board/config.h" +#ifdef PANDA_JUNGLE +#include "board/jungle/stm32h7/board.h" +#elif defined(PANDA_BODY) +#include "board/body/stm32h7/board.h" +#else +#include "board/stm32h7/board.h" +#endif + #include "board/drivers/led.h" #include "board/drivers/pwm.h" #include "board/drivers/usb.h" diff --git a/board/bootstub_declarations.h b/board/bootstub_declarations.h deleted file mode 100644 index 1b9580857c7..00000000000 --- a/board/bootstub_declarations.h +++ /dev/null @@ -1,17 +0,0 @@ -// ******************** Prototypes ******************** -void print(const char *a){ UNUSED(a); } -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; -void uart_init(uart_ring *q, int baud) { UNUSED(q); UNUSED(baud); } - -// ********************* Globals ********************** -uint8_t hw_type = 0; -board *current_board; diff --git a/board/bootstub_globals.c b/board/bootstub_globals.c new file mode 100644 index 00000000000..cc367ce76b5 --- /dev/null +++ b/board/bootstub_globals.c @@ -0,0 +1,27 @@ +#ifdef BOOTSTUB +// DRIVER_BUILD is defined here because this file is compiled with bs_env (not bs_drv_env) +// which doesn't have -DDRIVER_BUILD in its CFLAGS. Other bootstub driver files use bs_drv_env. +#define DRIVER_BUILD +#include "board/config.h" +#include "board/bootstub_globals.h" + +uint8_t hw_type = 0; +board *current_board; + +#ifdef PANDA_JUNGLE +uint8_t harness_orientation = 0U; +uint8_t can_mode = 0U; +uint8_t ignition = 0U; +#endif + +uart_ring uart_ring_som_debug; + +void print(const char *a){ UNUSED(a); } +void puth(unsigned int i){ UNUSED(i); } +void puth2(unsigned int i){ UNUSED(i); } +void puth4(unsigned int i){ UNUSED(i); } +void puthx(uint32_t i, uint8_t len) { UNUSED(i); UNUSED(len); } +void hexdump(const void *a, int l){ UNUSED(a); UNUSED(l); } +void putch(const char a) { UNUSED(a); } +void uart_init(uart_ring *q, unsigned int baud) { UNUSED(q); UNUSED(baud); } +#endif diff --git a/board/bootstub_globals.h b/board/bootstub_globals.h new file mode 100644 index 00000000000..200a69254b7 --- /dev/null +++ b/board/bootstub_globals.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +typedef struct board board; +typedef struct harness_configuration harness_configuration; +typedef struct uart_ring uart_ring; + +extern uint8_t hw_type; +extern board *current_board; + +extern uart_ring uart_ring_som_debug; + +void print(const char *a); +void puth(unsigned int i); +void puth2(unsigned int i); +void puth4(unsigned int i); +void puthx(uint32_t i, uint8_t len); +void hexdump(const void *a, int l); +void putch(const char a); +void uart_init(uart_ring *q, unsigned int baud); diff --git a/board/can_comms.c b/board/can_comms.c new file mode 100644 index 00000000000..16a575bf136 --- /dev/null +++ b/board/can_comms.c @@ -0,0 +1,149 @@ +#include "board/config.h" +#include "board/can_comms.h" +#include "board/drivers/can_common.h" +#include "board/libc.h" + +/* + CAN transactions to and from the host come in the form of + a certain number of CANPacket_t. The transaction is split + into multiple transfers or chunks. + + CAN packet byte layout (wire format used by comms_can_{read,write}): + +--------+--------+--------+--------+--------+--------+--------+------------------------------+ + | byte 0 | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | ... byte 13 / byte 69 | + +--------+--------+--------+--------+--------+--------+--------+------------------------------+ + | DLC | addr | addr | addr | flags | cksum | data0 | ... data7 / data63 | + | bus | | | | | | | (classic CAN / CAN FD) | + | fd | | | | | | | | + +--------+--------+--------+--------+--------+--------+--------+------------------------------+ + Byte/bit fields: + byte 0: DLC[7:4], bus[3:1], fd[0] + bytes 1..4: (addr << 3) | (extended << 2) | (returned << 1) | rejected + byte 5: checksum = XOR(header[0..4] + payload) + bytes 6..13 (classic CAN, up to 8 bytes) / bytes 6..69 (CAN FD, up to 64 bytes): payload + + USB/SPI transfer chunking used by this file: + +--------------------------------------------+ ... +--------------------------------------------+ + | transport chunk 0 | | transport chunk N | + +--------------------------------------------+ +--------------------------------------------+ + | concatenated CANPacket_t bytes | | continuation and/or next CANPacket_t bytes | + | (no per-64-byte counter/header in protocol)| | | + +--------------------------------------------+ +--------------------------------------------+ + + * comms_can_read outputs this buffer in chunks of a specified length. + chunks are always the given length, except the last one. + * comms_can_write reads in this buffer in chunks. + * both functions maintain an overflow buffer for a partial CANPacket_t that + spans multiple transfers/chunks. + * the overflow buffers are reset by a dedicated control transfer handler, + which is sent by the host on each start of a connection. +*/ + +typedef struct { + uint32_t ptr; + uint32_t tail_size; + uint8_t data[72]; +} asm_buffer; + +static asm_buffer can_read_buffer = {.ptr = 0U, .tail_size = 0U}; + +int comms_can_read(uint8_t *data, uint32_t max_len) { + uint32_t pos = 0U; + + // Send tail of previous message if it is in buffer + if (can_read_buffer.ptr > 0U) { + uint32_t overflow_len = MIN(max_len - pos, can_read_buffer.ptr); + (void)memcpy(&data[pos], can_read_buffer.data, overflow_len); + pos += overflow_len; + (void)memcpy(can_read_buffer.data, &can_read_buffer.data[overflow_len], can_read_buffer.ptr - overflow_len); + can_read_buffer.ptr -= overflow_len; + } + + if (can_read_buffer.ptr == 0U) { + // Fill rest of buffer with new data + CANPacket_t can_packet; + while ((pos < max_len) && can_pop(&can_rx_q, &can_packet)) { + uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[can_packet.data_len_code]; + if ((pos + pckt_len) <= max_len) { + (void)memcpy(&data[pos], (uint8_t*)&can_packet, pckt_len); + pos += pckt_len; + } else { + (void)memcpy(&data[pos], (uint8_t*)&can_packet, max_len - pos); + can_read_buffer.ptr += pckt_len - (max_len - pos); + // cppcheck-suppress objectIndex + (void)memcpy(can_read_buffer.data, &((uint8_t*)&can_packet)[(max_len - pos)], can_read_buffer.ptr); + pos = max_len; + } + } + } + + return pos; +} + +static asm_buffer can_write_buffer = {.ptr = 0U, .tail_size = 0U}; + +// send on CAN +void comms_can_write(const uint8_t *data, uint32_t len) { + uint32_t pos = 0U; + + // Assembling can message with data from buffer + if (can_write_buffer.ptr != 0U) { + if (can_write_buffer.tail_size <= (len - pos)) { + // we have enough data to complete the buffer + CANPacket_t to_push = {0}; + (void)memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], can_write_buffer.tail_size); + can_write_buffer.ptr += can_write_buffer.tail_size; + pos += can_write_buffer.tail_size; + + // send out + (void)memcpy((uint8_t*)&to_push, can_write_buffer.data, can_write_buffer.ptr); + can_send(&to_push, to_push.bus, false); + + // reset overflow buffer + can_write_buffer.ptr = 0U; + can_write_buffer.tail_size = 0U; + } else { + // maybe next time + uint32_t data_size = len - pos; + (void) memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], data_size); + can_write_buffer.tail_size -= data_size; + can_write_buffer.ptr += data_size; + pos += data_size; + } + } + + // rest of the message + while (pos < len) { + uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[(data[pos] >> 4U)]; + if ((pos + pckt_len) <= len) { + CANPacket_t to_push = {0}; + (void)memcpy((uint8_t*)&to_push, &data[pos], pckt_len); + can_send(&to_push, to_push.bus, false); + pos += pckt_len; + } else { + (void)memcpy(can_write_buffer.data, &data[pos], len - pos); + can_write_buffer.ptr = len - pos; + can_write_buffer.tail_size = pckt_len - can_write_buffer.ptr; + pos += can_write_buffer.ptr; + } + } + + refresh_can_tx_slots_available(); +} + +void comms_can_reset(void) { + can_write_buffer.ptr = 0U; + can_write_buffer.tail_size = 0U; + can_read_buffer.ptr = 0U; + can_read_buffer.tail_size = 0U; +} + +// TODO: make this more general! +void refresh_can_tx_slots_available(void) { + if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_USB_BULK_TRANSFER)) { + can_tx_comms_resume_usb(); + } + if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_SPI_BULK_TRANSFER)) { + can_tx_comms_resume_spi(); + } +} diff --git a/board/can_comms.h b/board/can_comms.h index f79c8ba4e46..ad967156e7a 100644 --- a/board/can_comms.h +++ b/board/can_comms.h @@ -1,144 +1,7 @@ -/* - CAN transactions to and from the host come in the form of - a certain number of CANPacket_t. The transaction is split - into multiple transfers or chunks. +#pragma once +// CAN communication buffer handling +// Implementations in can_comms.c - CAN packet byte layout (wire format used by comms_can_{read,write}): - +--------+--------+--------+--------+--------+--------+--------+------------------------------+ - | byte 0 | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | ... byte 13 / byte 69 | - +--------+--------+--------+--------+--------+--------+--------+------------------------------+ - | DLC | addr | addr | addr | flags | cksum | data0 | ... data7 / data63 | - | bus | | | | | | | (classic CAN / CAN FD) | - | fd | | | | | | | | - +--------+--------+--------+--------+--------+--------+--------+------------------------------+ - Byte/bit fields: - byte 0: DLC[7:4], bus[3:1], fd[0] - bytes 1..4: (addr << 3) | (extended << 2) | (returned << 1) | rejected - byte 5: checksum = XOR(header[0..4] + payload) - bytes 6..13 (classic CAN, up to 8 bytes) / bytes 6..69 (CAN FD, up to 64 bytes): payload - - USB/SPI transfer chunking used by this file: - +--------------------------------------------+ ... +--------------------------------------------+ - | transport chunk 0 | | transport chunk N | - +--------------------------------------------+ +--------------------------------------------+ - | concatenated CANPacket_t bytes | | continuation and/or next CANPacket_t bytes | - | (no per-64-byte counter/header in protocol)| | | - +--------------------------------------------+ +--------------------------------------------+ - - * comms_can_read outputs this buffer in chunks of a specified length. - chunks are always the given length, except the last one. - * comms_can_write reads in this buffer in chunks. - * both functions maintain an overflow buffer for a partial CANPacket_t that - spans multiple transfers/chunks. - * the overflow buffers are reset by a dedicated control transfer handler, - which is sent by the host on each start of a connection. -*/ - -typedef struct { - uint32_t ptr; - uint32_t tail_size; - uint8_t data[72]; -} asm_buffer; - -static asm_buffer can_read_buffer = {.ptr = 0U, .tail_size = 0U}; - -int comms_can_read(uint8_t *data, uint32_t max_len) { - uint32_t pos = 0U; - - // Send tail of previous message if it is in buffer - if (can_read_buffer.ptr > 0U) { - uint32_t overflow_len = MIN(max_len - pos, can_read_buffer.ptr); - (void)memcpy(&data[pos], can_read_buffer.data, overflow_len); - pos += overflow_len; - (void)memcpy(can_read_buffer.data, &can_read_buffer.data[overflow_len], can_read_buffer.ptr - overflow_len); - can_read_buffer.ptr -= overflow_len; - } - - if (can_read_buffer.ptr == 0U) { - // Fill rest of buffer with new data - CANPacket_t can_packet; - while ((pos < max_len) && can_pop(&can_rx_q, &can_packet)) { - uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[can_packet.data_len_code]; - if ((pos + pckt_len) <= max_len) { - (void)memcpy(&data[pos], (uint8_t*)&can_packet, pckt_len); - pos += pckt_len; - } else { - (void)memcpy(&data[pos], (uint8_t*)&can_packet, max_len - pos); - can_read_buffer.ptr += pckt_len - (max_len - pos); - // cppcheck-suppress objectIndex - (void)memcpy(can_read_buffer.data, &((uint8_t*)&can_packet)[(max_len - pos)], can_read_buffer.ptr); - pos = max_len; - } - } - } - - return pos; -} - -static asm_buffer can_write_buffer = {.ptr = 0U, .tail_size = 0U}; - -// send on CAN -void comms_can_write(const uint8_t *data, uint32_t len) { - uint32_t pos = 0U; - - // Assembling can message with data from buffer - if (can_write_buffer.ptr != 0U) { - if (can_write_buffer.tail_size <= (len - pos)) { - // we have enough data to complete the buffer - CANPacket_t to_push = {0}; - (void)memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], can_write_buffer.tail_size); - can_write_buffer.ptr += can_write_buffer.tail_size; - pos += can_write_buffer.tail_size; - - // send out - (void)memcpy((uint8_t*)&to_push, can_write_buffer.data, can_write_buffer.ptr); - can_send(&to_push, to_push.bus, false); - - // reset overflow buffer - can_write_buffer.ptr = 0U; - can_write_buffer.tail_size = 0U; - } else { - // maybe next time - uint32_t data_size = len - pos; - (void) memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], data_size); - can_write_buffer.tail_size -= data_size; - can_write_buffer.ptr += data_size; - pos += data_size; - } - } - - // rest of the message - while (pos < len) { - uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[(data[pos] >> 4U)]; - if ((pos + pckt_len) <= len) { - CANPacket_t to_push = {0}; - (void)memcpy((uint8_t*)&to_push, &data[pos], pckt_len); - can_send(&to_push, to_push.bus, false); - pos += pckt_len; - } else { - (void)memcpy(can_write_buffer.data, &data[pos], len - pos); - can_write_buffer.ptr = len - pos; - can_write_buffer.tail_size = pckt_len - can_write_buffer.ptr; - pos += can_write_buffer.ptr; - } - } - - refresh_can_tx_slots_available(); -} - -void comms_can_reset(void) { - can_write_buffer.ptr = 0U; - can_write_buffer.tail_size = 0U; - can_read_buffer.ptr = 0U; - can_read_buffer.tail_size = 0U; -} - -// TODO: make this more general! -void refresh_can_tx_slots_available(void) { - if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_USB_BULK_TRANSFER)) { - can_tx_comms_resume_usb(); - } - if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_SPI_BULK_TRANSFER)) { - can_tx_comms_resume_spi(); - } -} +void refresh_can_tx_slots_available(void); +void can_tx_comms_resume_usb(void); +void can_tx_comms_resume_spi(void); diff --git a/board/comms_definitions.h b/board/comms.h similarity index 95% rename from board/comms_definitions.h rename to board/comms.h index 8abc1f299a8..026a50aa6c9 100644 --- a/board/comms_definitions.h +++ b/board/comms.h @@ -1,5 +1,7 @@ #pragma once +#include + typedef struct { uint8_t request; uint16_t param1; diff --git a/board/crc.c b/board/crc.c new file mode 100644 index 00000000000..50b1b901810 --- /dev/null +++ b/board/crc.c @@ -0,0 +1,19 @@ +#include "board/crc.h" + +uint8_t crc_checksum(const uint8_t *dat, int len, const uint8_t poly) { + uint8_t crc = 0xFFU; + int i; + int j; + for (i = len - 1; i >= 0; i--) { + crc ^= dat[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80U) != 0U) { + crc = (uint8_t)((crc << 1) ^ poly); + } + else { + crc <<= 1; + } + } + } + return crc; +} diff --git a/board/crc.h b/board/crc.h index 3e20fb09817..05785093f6e 100644 --- a/board/crc.h +++ b/board/crc.h @@ -1,19 +1,5 @@ #pragma once -uint8_t crc_checksum(const uint8_t *dat, int len, const uint8_t poly) { - uint8_t crc = 0xFFU; - int i; - int j; - for (i = len - 1; i >= 0; i--) { - crc ^= dat[i]; - for (j = 0; j < 8; j++) { - if ((crc & 0x80U) != 0U) { - crc = (uint8_t)((crc << 1) ^ poly); - } - else { - crc <<= 1; - } - } - } - return crc; -} +#include + +uint8_t crc_checksum(const uint8_t *dat, int len, const uint8_t poly); diff --git a/board/drivers/bootkick.c b/board/drivers/bootkick.c new file mode 100644 index 00000000000..fb7d09f9a83 --- /dev/null +++ b/board/drivers/bootkick.c @@ -0,0 +1,69 @@ +#include "board/config.h" +#include "board/drivers/bootkick.h" + +bool bootkick_reset_triggered = false; + +void bootkick_tick(bool ignition, bool recent_heartbeat) { + static uint16_t bootkick_last_serial_ptr = 0; + static uint8_t waiting_to_boot_countdown = 0; + static uint8_t boot_reset_countdown = 0; + static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC; + static bool bootkick_ign_prev = false; + static BootState boot_state = BOOT_BOOTKICK; + BootState boot_state_prev = boot_state; + const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC); + + if ((ignition && !bootkick_ign_prev) || harness_inserted) { + // bootkick on rising edge of ignition or harness insertion + boot_state = BOOT_BOOTKICK; + } else if (recent_heartbeat) { + // disable bootkick once openpilot is up + boot_state = BOOT_STANDBY; + } else { + + } + + /* + Ensure SOM boots in case it goes into QDL mode. Reset behavior: + * shouldn't trigger on the first boot after power-on + * only try reset once per bootkick, i.e. don't keep trying until booted + * only try once per panda boot, since openpilot will reset panda on startup + * once BOOT_RESET is triggered, it stays until countdown is finished + */ + if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) { + waiting_to_boot_countdown = 20U; + } + if (waiting_to_boot_countdown > 0U) { + bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr; + if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) { + waiting_to_boot_countdown = 0U; + } else { + // try a reset + if (waiting_to_boot_countdown == 1U) { + boot_reset_countdown = 5U; + } + } + } + + // handle reset state + if (boot_reset_countdown > 0U) { + boot_state = BOOT_RESET; + bootkick_reset_triggered = true; + } else { + if (boot_state == BOOT_RESET) { + boot_state = BOOT_BOOTKICK; + } + } + + // update state + bootkick_ign_prev = ignition; + bootkick_harness_status_prev = harness.status; + bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx; + if (waiting_to_boot_countdown > 0U) { + waiting_to_boot_countdown--; + } + if (boot_reset_countdown > 0U) { + boot_reset_countdown--; + } + current_board->set_bootkick(boot_state); +} diff --git a/board/drivers/bootkick.h b/board/drivers/bootkick.h index 4bf990391b6..4793056c55b 100644 --- a/board/drivers/bootkick.h +++ b/board/drivers/bootkick.h @@ -1,68 +1,7 @@ -#include "board/drivers/drivers.h" +#pragma once -bool bootkick_reset_triggered = false; +#include -void bootkick_tick(bool ignition, bool recent_heartbeat) { - static uint16_t bootkick_last_serial_ptr = 0; - static uint8_t waiting_to_boot_countdown = 0; - static uint8_t boot_reset_countdown = 0; - static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC; - static bool bootkick_ign_prev = false; - static BootState boot_state = BOOT_BOOTKICK; - BootState boot_state_prev = boot_state; - const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC); +extern bool bootkick_reset_triggered; - if ((ignition && !bootkick_ign_prev) || harness_inserted) { - // bootkick on rising edge of ignition or harness insertion - boot_state = BOOT_BOOTKICK; - } else if (recent_heartbeat) { - // disable bootkick once openpilot is up - boot_state = BOOT_STANDBY; - } else { - - } - - /* - Ensure SOM boots in case it goes into QDL mode. Reset behavior: - * shouldn't trigger on the first boot after power-on - * only try reset once per bootkick, i.e. don't keep trying until booted - * only try once per panda boot, since openpilot will reset panda on startup - * once BOOT_RESET is triggered, it stays until countdown is finished - */ - if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) { - waiting_to_boot_countdown = 20U; - } - if (waiting_to_boot_countdown > 0U) { - bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr; - if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) { - waiting_to_boot_countdown = 0U; - } else { - // try a reset - if (waiting_to_boot_countdown == 1U) { - boot_reset_countdown = 5U; - } - } - } - - // handle reset state - if (boot_reset_countdown > 0U) { - boot_state = BOOT_RESET; - bootkick_reset_triggered = true; - } else { - if (boot_state == BOOT_RESET) { - boot_state = BOOT_BOOTKICK; - } - } - - // update state - bootkick_ign_prev = ignition; - bootkick_harness_status_prev = harness.status; - bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx; - if (waiting_to_boot_countdown > 0U) { - waiting_to_boot_countdown--; - } - if (boot_reset_countdown > 0U) { - boot_reset_countdown--; - } - current_board->set_bootkick(boot_state); -} +void bootkick_tick(bool ignition, bool recent_heartbeat); diff --git a/board/drivers/can_common.c b/board/drivers/can_common.c new file mode 100644 index 00000000000..81f889a1749 --- /dev/null +++ b/board/drivers/can_common.c @@ -0,0 +1,262 @@ +#include "board/config.h" +#include "board/drivers/can_common.h" +#include "board/can_comms.h" +#include "board/drivers/fdcan.h" + +uint32_t safety_tx_blocked = 0; +uint32_t safety_rx_invalid = 0; +uint32_t tx_buffer_overflow = 0; +uint32_t rx_buffer_overflow = 0; + +can_health_t can_health[PANDA_CAN_CNT] = {{0}, {0}, {0}}; + +// Ignition detected from CAN meessages +bool ignition_can = false; +uint32_t ignition_can_cnt = 0U; + +bool can_silent = true; +bool can_loopback = false; + +// ********************* instantiate queues ********************* +#define can_buffer(x, size) \ + static CANPacket_t elems_##x[size]; \ + extern can_ring can_##x; \ + can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = (size), .elems = (CANPacket_t *)&(elems_##x) }; + +#ifdef STM32H7 +// ITCM RAM and DTCM RAM are the fastest for Cortex-M7 core access +__attribute__((section(".axisram"))) can_buffer(rx_q, CAN_RX_BUFFER_SIZE) +__attribute__((section(".itcmram"))) can_buffer(tx1_q, CAN_TX_BUFFER_SIZE) +__attribute__((section(".itcmram"))) can_buffer(tx2_q, CAN_TX_BUFFER_SIZE) +#else // kept for PC +can_buffer(rx_q, CAN_RX_BUFFER_SIZE) +can_buffer(tx1_q, CAN_TX_BUFFER_SIZE) +can_buffer(tx2_q, CAN_TX_BUFFER_SIZE) +#endif +can_buffer(tx3_q, CAN_TX_BUFFER_SIZE) + +// FIXME: +// cppcheck-suppress misra-c2012-9.3 +can_ring *can_queues[PANDA_CAN_CNT] = {&can_tx1_q, &can_tx2_q, &can_tx3_q}; + +// ********************* interrupt safe queue ********************* +bool can_pop(can_ring *q, CANPacket_t *elem) { + bool ret = 0; + + ENTER_CRITICAL(); + if (q->w_ptr != q->r_ptr) { + *elem = q->elems[q->r_ptr]; + if ((q->r_ptr + 1U) == q->fifo_size) { + q->r_ptr = 0; + } else { + q->r_ptr += 1U; + } + ret = 1; + } + EXIT_CRITICAL(); + + return ret; +} + +bool can_push(can_ring *q, const CANPacket_t *elem) { + bool ret = false; + uint32_t next_w_ptr; + + ENTER_CRITICAL(); + if ((q->w_ptr + 1U) == q->fifo_size) { + next_w_ptr = 0; + } else { + next_w_ptr = q->w_ptr + 1U; + } + if (next_w_ptr != q->r_ptr) { + q->elems[q->w_ptr] = *elem; + q->w_ptr = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + if (!ret) { + #ifdef DEBUG + print("can_push to "); + if (q == &can_rx_q) { + print("can_rx_q"); + } else if (q == &can_tx1_q) { + print("can_tx1_q"); + } else if (q == &can_tx2_q) { + print("can_tx2_q"); + } else if (q == &can_tx3_q) { + print("can_tx3_q"); + } else { + print("unknown"); + } + print(" failed!\n"); + #endif + } + return ret; +} + +uint32_t can_slots_empty(const can_ring *q) { + uint32_t ret = 0; + + ENTER_CRITICAL(); + if (q->w_ptr >= q->r_ptr) { + ret = q->fifo_size - 1U - q->w_ptr + q->r_ptr; + } else { + ret = q->r_ptr - q->w_ptr - 1U; + } + EXIT_CRITICAL(); + + return ret; +} + +void can_clear(can_ring *q) { + ENTER_CRITICAL(); + q->w_ptr = 0; + q->r_ptr = 0; + EXIT_CRITICAL(); + // handle TX buffer full with zero ECUs awake on the bus + refresh_can_tx_slots_available(); +} + +// assign CAN numbering +// bus num: CAN Bus numbers in panda, sent to/from USB +// Min: 0; Max: 127; Bit 7 marks message as receipt (bus 129 is receipt for bus 1) +// cans: Look up MCU can interface from bus number +// can number: numeric lookup for MCU CAN interfaces (0 = CAN1, 1 = CAN2, etc); +// bus_lookup: Translates from 'can number' to 'bus number'. +// can_num_lookup: Translates from 'bus number' to 'can number'. +// forwarding bus: If >= 0, forward all messages from this bus to the specified bus. + +// Helpers +// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3 +bus_config_t bus_config[PANDA_CAN_CNT] = { + { .bus_lookup = 0U, .can_num_lookup = 0U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, + { .bus_lookup = 1U, .can_num_lookup = 1U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, + { .bus_lookup = 2U, .can_num_lookup = 2U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, +}; + +void can_init_all(void) { + for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) { + bus_config[i].canfd_enabled = false; + can_clear(can_queues[i]); + (void)can_init(i); + } +} + +void can_set_orientation(bool flipped) { + bus_config[0].bus_lookup = flipped ? 2U : 0U; + bus_config[0].can_num_lookup = flipped ? 2U : 0U; + bus_config[2].bus_lookup = flipped ? 0U : 2U; + bus_config[2].can_num_lookup = flipped ? 0U : 2U; +} + +#ifdef PANDA_JUNGLE +void can_set_forwarding(uint8_t from, uint8_t to) { + bus_config[from].forwarding_bus = to; +} +#endif + +void ignition_can_hook(CANPacket_t *msg) { + if (msg->bus == 0U) { + int len = GET_LEN(msg); + + // GM exception + if ((msg->addr == 0x1F1U) && (len == 8)) { + // SystemPowerMode (2=Run, 3=Crank Request) + ignition_can = (msg->data[0] & 0x2U) != 0U; + ignition_can_cnt = 0U; + } + + // Rivian R1S/T GEN1 exception + if ((msg->addr == 0x152U) && (len == 8)) { + // 0x152 overlaps with Subaru pre-global which has this bit as the high beam + int counter = msg->data[1] & 0xFU; // max is only 14 + + static int prev_counter_rivian = -1; + if ((counter == ((prev_counter_rivian + 1) % 15)) && (prev_counter_rivian != -1)) { + // VDM_OutputSignals->VDM_EpasPowerMode + ignition_can = ((msg->data[7] >> 4U) & 0x3U) == 1U; // VDM_EpasPowerMode_Drive_On=1 + ignition_can_cnt = 0U; + } + prev_counter_rivian = counter; + } + + // Tesla Model 3/Y exception + if ((msg->addr == 0x221U) && (len == 8)) { + // 0x221 overlaps with Rivian which has random data on byte 0 + int counter = msg->data[6] >> 4; + + static int prev_counter_tesla = -1; + if ((counter == ((prev_counter_tesla + 1) % 16)) && (prev_counter_tesla != -1)) { + // VCFRONT_LVPowerState->VCFRONT_vehiclePowerState + int power_state = (msg->data[0] >> 5U) & 0x3U; + ignition_can = power_state == 0x3; // VEHICLE_POWER_STATE_DRIVE=3 + ignition_can_cnt = 0U; + } + prev_counter_tesla = counter; + } + + // Mazda exception + if ((msg->addr == 0x9EU) && (len == 8)) { + ignition_can = (msg->data[0] >> 5) == 0x6U; + ignition_can_cnt = 0U; + } + } + + // body v2 exception + if (((msg->bus == 0U) || (msg->bus == 2U)) && (msg->addr == 0x222U)) { + ignition_can = true; + ignition_can_cnt = 0U; + } +} + +bool can_tx_check_min_slots_free(uint32_t min) { + return + (can_slots_empty(&can_tx1_q) >= min) && + (can_slots_empty(&can_tx2_q) >= min) && + (can_slots_empty(&can_tx3_q) >= min); +} + +uint8_t calculate_checksum(const uint8_t *dat, uint32_t len) { + uint8_t checksum = 0U; + for (uint32_t i = 0U; i < len; i++) { + checksum ^= dat[i]; + } + return checksum; +} + +void can_set_checksum(CANPacket_t *packet) { + packet->checksum = 0U; + packet->checksum = calculate_checksum((uint8_t *) packet, CANPACKET_HEAD_SIZE + GET_LEN(packet)); +} + +bool can_check_checksum(CANPacket_t *packet) { + return (calculate_checksum((uint8_t *) packet, CANPACKET_HEAD_SIZE + GET_LEN(packet)) == 0U); +} + +void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook) { + if (skip_tx_hook || safety_tx_hook(to_push) != 0) { + if (bus_number < PANDA_CAN_CNT) { + // add CAN packet to send queue + tx_buffer_overflow += can_push(can_queues[bus_number], to_push) ? 0U : 1U; + process_can(CAN_NUM_FROM_BUS_NUM(bus_number)); + } + } else { + safety_tx_blocked += 1U; + to_push->returned = 0U; + to_push->rejected = 1U; + + // data changed + can_set_checksum(to_push); + rx_buffer_overflow += can_push(&can_rx_q, to_push) ? 0U : 1U; + } +} + +bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len) { + bool ret = false; + for (uint8_t i = 0U; i < len; i++) { + if (all_speeds[i] == speed) { + ret = true; + } + } + return ret; +} diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index bbb3bc92818..0d015439e68 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -1,262 +1,80 @@ -#include "board/drivers/drivers.h" +#pragma once -uint32_t safety_tx_blocked = 0; -uint32_t safety_rx_invalid = 0; -uint32_t tx_buffer_overflow = 0; -uint32_t rx_buffer_overflow = 0; +#include +#include -can_health_t can_health[PANDA_CAN_CNT] = {{0}, {0}, {0}}; - -// Ignition detected from CAN meessages -bool ignition_can = false; -uint32_t ignition_can_cnt = 0U; - -bool can_silent = true; -bool can_loopback = false; - -// ********************* instantiate queues ********************* -#define can_buffer(x, size) \ - static CANPacket_t elems_##x[size]; \ - extern can_ring can_##x; \ - can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = (size), .elems = (CANPacket_t *)&(elems_##x) }; +#include "board/can.h" +#include "board/health.h" #define CAN_RX_BUFFER_SIZE 4096U #define CAN_TX_BUFFER_SIZE 416U -#ifdef STM32H7 -// ITCM RAM and DTCM RAM are the fastest for Cortex-M7 core access -__attribute__((section(".axisram"))) can_buffer(rx_q, CAN_RX_BUFFER_SIZE) -__attribute__((section(".itcmram"))) can_buffer(tx1_q, CAN_TX_BUFFER_SIZE) -__attribute__((section(".itcmram"))) can_buffer(tx2_q, CAN_TX_BUFFER_SIZE) -#else // kept for PC -can_buffer(rx_q, CAN_RX_BUFFER_SIZE) -can_buffer(tx1_q, CAN_TX_BUFFER_SIZE) -can_buffer(tx2_q, CAN_TX_BUFFER_SIZE) -#endif -can_buffer(tx3_q, CAN_TX_BUFFER_SIZE) - -// FIXME: -// cppcheck-suppress misra-c2012-9.3 -can_ring *can_queues[PANDA_CAN_CNT] = {&can_tx1_q, &can_tx2_q, &can_tx3_q}; - -// ********************* interrupt safe queue ********************* -bool can_pop(can_ring *q, CANPacket_t *elem) { - bool ret = 0; - - ENTER_CRITICAL(); - if (q->w_ptr != q->r_ptr) { - *elem = q->elems[q->r_ptr]; - if ((q->r_ptr + 1U) == q->fifo_size) { - q->r_ptr = 0; - } else { - q->r_ptr += 1U; - } - ret = 1; - } - EXIT_CRITICAL(); - - return ret; -} - -bool can_push(can_ring *q, const CANPacket_t *elem) { - bool ret = false; - uint32_t next_w_ptr; - - ENTER_CRITICAL(); - if ((q->w_ptr + 1U) == q->fifo_size) { - next_w_ptr = 0; - } else { - next_w_ptr = q->w_ptr + 1U; - } - if (next_w_ptr != q->r_ptr) { - q->elems[q->w_ptr] = *elem; - q->w_ptr = next_w_ptr; - ret = true; - } - EXIT_CRITICAL(); - if (!ret) { - #ifdef DEBUG - print("can_push to "); - if (q == &can_rx_q) { - print("can_rx_q"); - } else if (q == &can_tx1_q) { - print("can_tx1_q"); - } else if (q == &can_tx2_q) { - print("can_tx2_q"); - } else if (q == &can_tx3_q) { - print("can_tx3_q"); - } else { - print("unknown"); - } - print(" failed!\n"); - #endif - } - return ret; -} - -uint32_t can_slots_empty(const can_ring *q) { - uint32_t ret = 0; - - ENTER_CRITICAL(); - if (q->w_ptr >= q->r_ptr) { - ret = q->fifo_size - 1U - q->w_ptr + q->r_ptr; - } else { - ret = q->r_ptr - q->w_ptr - 1U; - } - EXIT_CRITICAL(); - - return ret; -} - -void can_clear(can_ring *q) { - ENTER_CRITICAL(); - q->w_ptr = 0; - q->r_ptr = 0; - EXIT_CRITICAL(); - // handle TX buffer full with zero ECUs awake on the bus - refresh_can_tx_slots_available(); -} - -// assign CAN numbering -// bus num: CAN Bus numbers in panda, sent to/from USB -// Min: 0; Max: 127; Bit 7 marks message as receipt (bus 129 is receipt for but 1) -// cans: Look up MCU can interface from bus number -// can number: numeric lookup for MCU CAN interfaces (0 = CAN1, 1 = CAN2, etc); -// bus_lookup: Translates from 'can number' to 'bus number'. -// can_num_lookup: Translates from 'bus number' to 'can number'. -// forwarding bus: If >= 0, forward all messages from this bus to the specified bus. - -// Helpers -// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3 -bus_config_t bus_config[PANDA_CAN_CNT] = { - { .bus_lookup = 0U, .can_num_lookup = 0U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, - { .bus_lookup = 1U, .can_num_lookup = 1U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, - { .bus_lookup = 2U, .can_num_lookup = 2U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, -}; - -void can_init_all(void) { - for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) { - bus_config[i].canfd_enabled = false; - can_clear(can_queues[i]); - (void)can_init(i); - } -} - -void can_set_orientation(bool flipped) { - bus_config[0].bus_lookup = flipped ? 2U : 0U; - bus_config[0].can_num_lookup = flipped ? 2U : 0U; - bus_config[2].bus_lookup = flipped ? 0U : 2U; - bus_config[2].can_num_lookup = flipped ? 0U : 2U; -} +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; + +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); +void can_clear(can_ring *q); + +extern can_ring can_rx_q; +extern can_ring can_tx1_q; +extern can_ring can_tx2_q; +extern can_ring can_tx3_q; +extern can_ring *can_queues[PANDA_CAN_CNT]; +extern bus_config_t bus_config[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)) + +#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) + +// safety forward declarations (defined in opendbc/safety) +bool safety_tx_hook(CANPacket_t *msg); +bool safety_rx_hook(const CANPacket_t *msg); +int safety_fwd_hook(int bus_num, int addr); + +void can_init_all(void); +void can_set_orientation(bool flipped); #ifdef PANDA_JUNGLE -void can_set_forwarding(uint8_t from, uint8_t to) { - bus_config[from].forwarding_bus = to; -} +void can_set_forwarding(uint8_t from, uint8_t to); #endif - -void ignition_can_hook(CANPacket_t *msg) { - if (msg->bus == 0U) { - int len = GET_LEN(msg); - - // GM exception - if ((msg->addr == 0x1F1U) && (len == 8)) { - // SystemPowerMode (2=Run, 3=Crank Request) - ignition_can = (msg->data[0] & 0x2U) != 0U; - ignition_can_cnt = 0U; - } - - // Rivian R1S/T GEN1 exception - if ((msg->addr == 0x152U) && (len == 8)) { - // 0x152 overlaps with Subaru pre-global which has this bit as the high beam - int counter = msg->data[1] & 0xFU; // max is only 14 - - static int prev_counter_rivian = -1; - if ((counter == ((prev_counter_rivian + 1) % 15)) && (prev_counter_rivian != -1)) { - // VDM_OutputSignals->VDM_EpasPowerMode - ignition_can = ((msg->data[7] >> 4U) & 0x3U) == 1U; // VDM_EpasPowerMode_Drive_On=1 - ignition_can_cnt = 0U; - } - prev_counter_rivian = counter; - } - - // Tesla Model 3/Y exception - if ((msg->addr == 0x221U) && (len == 8)) { - // 0x221 overlaps with Rivian which has random data on byte 0 - int counter = msg->data[6] >> 4; - - static int prev_counter_tesla = -1; - if ((counter == ((prev_counter_tesla + 1) % 16)) && (prev_counter_tesla != -1)) { - // VCFRONT_LVPowerState->VCFRONT_vehiclePowerState - int power_state = (msg->data[0] >> 5U) & 0x3U; - ignition_can = power_state == 0x3; // VEHICLE_POWER_STATE_DRIVE=3 - ignition_can_cnt = 0U; - } - prev_counter_tesla = counter; - } - - // Mazda exception - if ((msg->addr == 0x9EU) && (len == 8)) { - ignition_can = (msg->data[0] >> 5) == 0x6U; - ignition_can_cnt = 0U; - } - } - - // body v2 exception - if (((msg->bus == 0U) || (msg->bus == 2U)) && (msg->addr == 0x222U)) { - ignition_can = true; - ignition_can_cnt = 0U; - } -} - -bool can_tx_check_min_slots_free(uint32_t min) { - return - (can_slots_empty(&can_tx1_q) >= min) && - (can_slots_empty(&can_tx2_q) >= min) && - (can_slots_empty(&can_tx3_q) >= min); -} - -uint8_t calculate_checksum(const uint8_t *dat, uint32_t len) { - uint8_t checksum = 0U; - for (uint32_t i = 0U; i < len; i++) { - checksum ^= dat[i]; - } - return checksum; -} - -void can_set_checksum(CANPacket_t *packet) { - packet->checksum = 0U; - packet->checksum = calculate_checksum((uint8_t *) packet, CANPACKET_HEAD_SIZE + GET_LEN(packet)); -} - -bool can_check_checksum(CANPacket_t *packet) { - return (calculate_checksum((uint8_t *) packet, CANPACKET_HEAD_SIZE + GET_LEN(packet)) == 0U); -} - -void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook) { - if (skip_tx_hook || safety_tx_hook(to_push) != 0) { - if (bus_number < PANDA_CAN_CNT) { - // add CAN packet to send queue - tx_buffer_overflow += can_push(can_queues[bus_number], to_push) ? 0U : 1U; - process_can(CAN_NUM_FROM_BUS_NUM(bus_number)); - } - } else { - safety_tx_blocked += 1U; - to_push->returned = 0U; - to_push->rejected = 1U; - - // data changed - can_set_checksum(to_push); - rx_buffer_overflow += can_push(&can_rx_q, to_push) ? 0U : 1U; - } -} - -bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len) { - bool ret = false; - for (uint8_t i = 0U; i < len; i++) { - if (all_speeds[i] == speed) { - ret = true; - } - } - return ret; -} +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); diff --git a/board/drivers/clock_source.c b/board/drivers/clock_source.c new file mode 100644 index 00000000000..4db97e5a4de --- /dev/null +++ b/board/drivers/clock_source.c @@ -0,0 +1,63 @@ +#include "board/config.h" +#include "board/drivers/clock_source.h" + +void clock_source_set_timer_params(uint16_t param1, uint16_t param2) { + // Pulse length of each channel + register_set(&(TIM1->CCR1), (((param1 & 0xFF00U) >> 8U)*10U), 0xFFFFU); + register_set(&(TIM1->CCR2), ((param1 & 0x00FFU)*10U), 0xFFFFU); + register_set(&(TIM8->CCR3), (((param2 & 0xFF00U) >> 8U)*10U), 0xFFFFU); + // Timer period + register_set(&(TIM1->ARR), (((param2 & 0x00FFU)*10U) - 1U), 0xFFFFU); + register_set(&(TIM1->CCR4), ((TIM1->ARR + 1U) / 2U), 0xFFFFU); +} + +void clock_source_init(bool enable_channel1) { + // Setup timer + register_set(&(TIM1->PSC), ((APB2_TIMER_FREQ*100U)-1U), 0xFFFFU); // Tick on 0.1 ms + register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); // Period + register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); // No output on compare + register_set(&(TIM1->CCER), TIM_CCER_CC1E | TIM_CCER_CC2NE, 0xFFFFU); // Enable compares + register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value + register_set(&(TIM1->CCR2), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 2 value + register_set(&(TIM1->CCR4), (CLOCK_SOURCE_PERIOD_MS*5U), 0xFFFFU); // For slave timer + register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); // Enable interrupts + + // No interrupts + NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); + NVIC_DisableIRQ(TIM1_CC_IRQn); + + // Set GPIO as timer channels + if (enable_channel1) { + set_gpio_alternate(GPIOA, 8, GPIO_AF1_TIM1); + } + set_gpio_alternate(GPIOB, 14, GPIO_AF1_TIM1); + + // Set PWM mode + register_set(&(TIM1->CCMR1), (0b110UL << TIM_CCMR1_OC1M_Pos) | (0b110UL << TIM_CCMR1_OC2M_Pos), 0xFFFFU); + register_set(&(TIM1->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos) | (0b111UL << TIM_CCMR2_OC4M_Pos), 0xFFFFU); + + // Enable output + register_set(&(TIM1->BDTR), TIM_BDTR_MOE, 0xFFFFU); + + // Sync with slave + register_set(&(TIM1->SMCR), TIM_SMCR_MSM , 0xFFFFU); + register_set(&(TIM1->CR2), (0b0111U << TIM_CR2_MMS_Pos), 0xFFFFU); + register_set(&(TIM8->SMCR), (0b0100U << TIM_SMCR_SMS_Pos) | (0b000U << TIM_SMCR_TS_Pos), 0xFFFFU); + + // Setup slave timer (TIM8) + register_set(&(TIM8->PSC), TIM1->PSC, 0xFFFFU); + register_set(&(TIM8->ARR), TIM1->ARR, 0xFFFFU); + register_set(&(TIM8->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos), 0xFFFFU); + register_set(&(TIM8->CCR3), (CLOCK_SOURCE_PULSE_LEN_MS * 10U), 0xFFFFU); + register_set(&(TIM8->CCER), TIM_CCER_CC3NE, 0xFFFFU); + + // MOE for TIM8 as well + register_set(&(TIM8->BDTR), TIM_BDTR_MOE, 0xFFFFU); + + // Set GPIO + set_gpio_alternate(GPIOB, 15, GPIO_AF3_TIM8); + + // Enable timers + register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); + register_set(&(TIM8->CR1), TIM_CR1_CEN, 0x3FU); +} diff --git a/board/drivers/clock_source.h b/board/drivers/clock_source.h index bd05d414596..0a94ed21f62 100644 --- a/board/drivers/clock_source.h +++ b/board/drivers/clock_source.h @@ -1,66 +1,10 @@ -#include "board/drivers/drivers.h" +#pragma once + +#include +#include #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 - register_set(&(TIM1->CCR1), (((param1 & 0xFF00U) >> 8U)*10U), 0xFFFFU); - register_set(&(TIM1->CCR2), ((param1 & 0x00FFU)*10U), 0xFFFFU); - register_set(&(TIM8->CCR3), (((param2 & 0xFF00U) >> 8U)*10U), 0xFFFFU); - // Timer period - register_set(&(TIM1->ARR), (((param2 & 0x00FFU)*10U) - 1U), 0xFFFFU); - register_set(&(TIM1->CCR4), ((TIM1->ARR + 1U) / 2U), 0xFFFFU); -} - -void clock_source_init(bool enable_channel1) { - // Setup timer - register_set(&(TIM1->PSC), ((APB2_TIMER_FREQ*100U)-1U), 0xFFFFU); // Tick on 0.1 ms - register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); // Period - register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); // No output on compare - register_set(&(TIM1->CCER), TIM_CCER_CC1E | TIM_CCER_CC2NE, 0xFFFFU); // Enable compares - register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value - register_set(&(TIM1->CCR2), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 2 value - register_set(&(TIM1->CCR4), (CLOCK_SOURCE_PERIOD_MS*5U), 0xFFFFU); // For slave timer - register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); // Enable interrupts - - - // No interrupts - NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); - NVIC_DisableIRQ(TIM1_CC_IRQn); - - // Set GPIO as timer channels - if (enable_channel1) { - set_gpio_alternate(GPIOA, 8, GPIO_AF1_TIM1); - } - set_gpio_alternate(GPIOB, 14, GPIO_AF1_TIM1); - - // Set PWM mode - register_set(&(TIM1->CCMR1), (0b110UL << TIM_CCMR1_OC1M_Pos) | (0b110UL << TIM_CCMR1_OC2M_Pos), 0xFFFFU); - register_set(&(TIM1->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos) | (0b111UL << TIM_CCMR2_OC4M_Pos), 0xFFFFU); - - // Enable output - register_set(&(TIM1->BDTR), TIM_BDTR_MOE, 0xFFFFU); - - // Sync with slave - register_set(&(TIM1->SMCR), TIM_SMCR_MSM , 0xFFFFU); - register_set(&(TIM1->CR2), (0b0111U << TIM_CR2_MMS_Pos), 0xFFFFU); - register_set(&(TIM8->SMCR), (0b0100U << TIM_SMCR_SMS_Pos) | (0b000U << TIM_SMCR_TS_Pos), 0xFFFFU); - - // Setup slave timer (TIM8) - register_set(&(TIM8->PSC), TIM1->PSC, 0xFFFFU); - register_set(&(TIM8->ARR), TIM1->ARR, 0xFFFFU); - register_set(&(TIM8->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos), 0xFFFFU); - register_set(&(TIM8->CCR3), (CLOCK_SOURCE_PULSE_LEN_MS * 10U), 0xFFFFU); - register_set(&(TIM8->CCER), TIM_CCER_CC3NE, 0xFFFFU); - - // MOE for TIM8 as well - register_set(&(TIM8->BDTR), TIM_BDTR_MOE, 0xFFFFU); - - // Set GPIO - set_gpio_alternate(GPIOB, 15, GPIO_AF3_TIM8); - - // Enable timers - register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); - register_set(&(TIM8->CR1), TIM_CR1_CEN, 0x3FU); -} +void clock_source_set_timer_params(uint16_t param1, uint16_t param2); +void clock_source_init(bool enable_channel1); diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h deleted file mode 100644 index c61506707a0..00000000000 --- a/board/drivers/drivers.h +++ /dev/null @@ -1,287 +0,0 @@ -#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); diff --git a/board/drivers/fake_siren.c b/board/drivers/fake_siren.c new file mode 100644 index 00000000000..503edb0f41a --- /dev/null +++ b/board/drivers/fake_siren.c @@ -0,0 +1,115 @@ +#include "board/config.h" +#include "board/drivers/fake_siren.h" +#include "board/stm32h7/lli2c.h" + +void siren_tim7_init(void) { + // Init trigger timer (around 2.5kHz) + register_set(&TIM7->PSC, 0U, 0xFFFFU); + register_set(&TIM7->ARR, 133U, 0xFFFFU); + register_set(&TIM7->CR2, (0b10U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); + register_set(&TIM7->CR1, TIM_CR1_ARPE | TIM_CR1_URS, 0x088EU); + TIM7->SR = 0U; + TIM7->CR1 |= TIM_CR1_CEN; +} + +void siren_dac_init(void) { + DAC1->DHR8R1 = (1U << 7); + DAC1->DHR8R2 = (1U << 7); + register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); + register_set(&DAC1->CR, DAC_CR_TEN1 | (6U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); + register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); +} + +void siren_dma_init(void) { + // 1Vpp sine wave with 1V offset + static const uint8_t fake_siren_lut[360] = { 134U, 135U, 137U, 138U, 139U, 140U, 141U, 143U, 144U, 145U, 146U, 148U, 149U, 150U, 151U, 152U, 154U, 155U, 156U, 157U, 158U, 159U, 160U, 162U, 163U, 164U, 165U, 166U, 167U, 168U, 169U, 170U, 171U, 172U, 174U, 175U, 176U, 177U, 177U, 178U, 179U, 180U, 181U, 182U, 183U, 184U, 185U, 186U, 186U, 187U, 188U, 189U, 190U, 190U, 191U, 192U, 193U, 193U, 194U, 195U, 195U, 196U, 196U, 197U, 197U, 198U, 199U, 199U, 199U, 200U, 200U, 201U, 201U, 202U, 202U, 202U, 203U, 203U, 203U, 203U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 205U, 205U, 205U, 205U, 205U, 205U, 205U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 203U, 203U, 203U, 203U, 202U, 202U, 202U, 201U, 201U, 200U, 200U, 199U, 199U, 199U, 198U, 197U, 197U, 196U, 196U, 195U, 195U, 194U, 193U, 193U, 192U, 191U, 190U, 190U, 189U, 188U, 187U, 186U, 186U, 185U, 184U, 183U, 182U, 181U, 180U, 179U, 178U, 177U, 177U, 176U, 175U, 174U, 172U, 171U, 170U, 169U, 168U, 167U, 166U, 165U, 164U, 163U, 162U, 160U, 159U, 158U, 157U, 156U, 155U, 154U, 152U, 151U, 150U, 149U, 148U, 146U, 145U, 144U, 143U, 141U, 140U, 139U, 138U, 137U, 135U, 134U, 133U, 132U, 130U, 129U, 128U, 127U, 125U, 124U, 123U, 122U, 121U, 119U, 118U, 117U, 116U, 115U, 113U, 112U, 111U, 110U, 109U, 108U, 106U, 105U, 104U, 103U, 102U, 101U, 100U, 99U, 98U, 97U, 96U, 95U, 94U, 93U, 92U, 91U, 90U, 89U, 88U, 87U, 86U, 85U, 84U, 83U, 82U, 82U, 81U, 80U, 79U, 78U, 78U, 77U, 76U, 76U, 75U, 74U, 74U, 73U, 72U, 72U, 71U, 71U, 70U, 70U, 69U, 69U, 68U, 68U, 67U, 67U, 67U, 66U, 66U, 66U, 65U, 65U, 65U, 65U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 63U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 65U, 65U, 65U, 65U, 66U, 66U, 66U, 67U, 67U, 67U, 68U, 68U, 69U, 69U, 70U, 70U, 71U, 71U, 72U, 72U, 73U, 74U, 74U, 75U, 76U, 76U, 77U, 78U, 78U, 79U, 80U, 81U, 82U, 82U, 83U, 84U, 85U, 86U, 87U, 88U, 89U, 90U, 91U, 92U, 93U, 94U, 95U, 96U, 97U, 98U, 99U, 100U, 101U, 102U, 103U, 104U, 105U, 106U, 108U, 109U, 110U, 111U, 112U, 113U, 115U, 116U, 117U, 118U, 119U, 121U, 122U, 123U, 124U, 125U, 127U, 128U, 129U, 130U, 132U, 133U }; + // Setup DMAMUX (DAC_CH1_DMA as input) + register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); + register_set(&DMA1_Stream1->PAR, (uint32_t)&(DAC1->DHR8R1), 0xFFFFFFFFU); + // Setup DMA + register_set(&DMA1_Stream1->M0AR, (uint32_t)fake_siren_lut, 0xFFFFFFFFU); + register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); + DMA1_Stream1->NDTR = sizeof(fake_siren_lut); + DMA1_Stream1->CR = (0b11UL << DMA_SxCR_PL_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC | (1U << DMA_SxCR_DIR_Pos); +} + +void fake_siren_codec_enable(bool enabled) { + if (enabled) { + bool success = true; + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2B, (1U << 1)); // Left speaker mix from INA1 + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2C, (1U << 1)); // Right speaker mix from INA1 + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3D, 0x17, 0b11111); // Left speaker volume + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3E, 0x17, 0b11111); // Right speaker volume + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x37, 0b101, 0b111); // INA gain + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7)); // Enable INA + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x51, (1U << 7)); // Disable global shutdown + if (!success) { + print("Siren codec enable failed\n"); + fault_occurred(FAULT_SIREN_MALFUNCTION); + } + } else { + // Disable INA input. Make sure to retry a few times if the I2C bus is busy. + for (uint8_t i=0U; i<10U; i++) { + if (i2c_clear_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7))) { + break; + } + } + } +} + +static void fake_i2c_siren_init(void) { + siren_dac_init(); + siren_dma_init(); + siren_tim7_init(); + // Enable the I2C to the codec + i2c_init(I2C5); + fake_siren_codec_enable(false); +} + +void fake_i2c_siren_set(bool enabled) { + static bool initialized = false; + static bool fake_siren_enabled = false; + + if (!initialized) { + fake_i2c_siren_init(); + initialized = true; + } + + if (enabled != fake_siren_enabled) { + fake_siren_codec_enable(enabled); + } + + if (enabled) { + register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } else { + register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } + fake_siren_enabled = enabled; +} + +void fake_siren_set(bool enabled) { + static bool initialized = false; + static bool fake_siren_enabled = false; + + if (!initialized) { + siren_tim7_init(); + initialized = true; + } + + if (enabled != fake_siren_enabled) { + if (enabled) { + sound_stop_dac(); + siren_dac_init(); + siren_dma_init(); + current_board->set_amp_enabled(true); + register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } else { + current_board->set_amp_enabled(false); + // Stop modified 8-bit DAC and start normal 12-bit DAC + sound_stop_dac(); + sound_init_dac(); + register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); + } + fake_siren_enabled = enabled; + } +} diff --git a/board/drivers/fake_siren.h b/board/drivers/fake_siren.h index 3a4d8a36a57..d389d368dc7 100644 --- a/board/drivers/fake_siren.h +++ b/board/drivers/fake_siren.h @@ -1,115 +1,10 @@ -#include "board/stm32h7/lli2c.h" +#pragma once #define CODEC_I2C_ADDR 0x10 -void siren_tim7_init(void) { - // Init trigger timer (around 2.5kHz) - register_set(&TIM7->PSC, 0U, 0xFFFFU); - register_set(&TIM7->ARR, 133U, 0xFFFFU); - register_set(&TIM7->CR2, (0b10U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); - register_set(&TIM7->CR1, TIM_CR1_ARPE | TIM_CR1_URS, 0x088EU); - TIM7->SR = 0U; - TIM7->CR1 |= TIM_CR1_CEN; -} - -void siren_dac_init(void) { - DAC1->DHR8R1 = (1U << 7); - DAC1->DHR8R2 = (1U << 7); - register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); - register_set(&DAC1->CR, DAC_CR_TEN1 | (6U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); - register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); -} - -void siren_dma_init(void) { - // 1Vpp sine wave with 1V offset - static const uint8_t fake_siren_lut[360] = { 134U, 135U, 137U, 138U, 139U, 140U, 141U, 143U, 144U, 145U, 146U, 148U, 149U, 150U, 151U, 152U, 154U, 155U, 156U, 157U, 158U, 159U, 160U, 162U, 163U, 164U, 165U, 166U, 167U, 168U, 169U, 170U, 171U, 172U, 174U, 175U, 176U, 177U, 177U, 178U, 179U, 180U, 181U, 182U, 183U, 184U, 185U, 186U, 186U, 187U, 188U, 189U, 190U, 190U, 191U, 192U, 193U, 193U, 194U, 195U, 195U, 196U, 196U, 197U, 197U, 198U, 199U, 199U, 199U, 200U, 200U, 201U, 201U, 202U, 202U, 202U, 203U, 203U, 203U, 203U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 205U, 205U, 205U, 205U, 205U, 205U, 205U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 203U, 203U, 203U, 203U, 202U, 202U, 202U, 201U, 201U, 200U, 200U, 199U, 199U, 199U, 198U, 197U, 197U, 196U, 196U, 195U, 195U, 194U, 193U, 193U, 192U, 191U, 190U, 190U, 189U, 188U, 187U, 186U, 186U, 185U, 184U, 183U, 182U, 181U, 180U, 179U, 178U, 177U, 177U, 176U, 175U, 174U, 172U, 171U, 170U, 169U, 168U, 167U, 166U, 165U, 164U, 163U, 162U, 160U, 159U, 158U, 157U, 156U, 155U, 154U, 152U, 151U, 150U, 149U, 148U, 146U, 145U, 144U, 143U, 141U, 140U, 139U, 138U, 137U, 135U, 134U, 133U, 132U, 130U, 129U, 128U, 127U, 125U, 124U, 123U, 122U, 121U, 119U, 118U, 117U, 116U, 115U, 113U, 112U, 111U, 110U, 109U, 108U, 106U, 105U, 104U, 103U, 102U, 101U, 100U, 99U, 98U, 97U, 96U, 95U, 94U, 93U, 92U, 91U, 90U, 89U, 88U, 87U, 86U, 85U, 84U, 83U, 82U, 82U, 81U, 80U, 79U, 78U, 78U, 77U, 76U, 76U, 75U, 74U, 74U, 73U, 72U, 72U, 71U, 71U, 70U, 70U, 69U, 69U, 68U, 68U, 67U, 67U, 67U, 66U, 66U, 66U, 65U, 65U, 65U, 65U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 63U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 65U, 65U, 65U, 65U, 66U, 66U, 66U, 67U, 67U, 67U, 68U, 68U, 69U, 69U, 70U, 70U, 71U, 71U, 72U, 72U, 73U, 74U, 74U, 75U, 76U, 76U, 77U, 78U, 78U, 79U, 80U, 81U, 82U, 82U, 83U, 84U, 85U, 86U, 87U, 88U, 89U, 90U, 91U, 92U, 93U, 94U, 95U, 96U, 97U, 98U, 99U, 100U, 101U, 102U, 103U, 104U, 105U, 106U, 108U, 109U, 110U, 111U, 112U, 113U, 115U, 116U, 117U, 118U, 119U, 121U, 122U, 123U, 124U, 125U, 127U, 128U, 129U, 130U, 132U, 133U }; - // Setup DMAMUX (DAC_CH1_DMA as input) - register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); - register_set(&DMA1_Stream1->PAR, (uint32_t)&(DAC1->DHR8R1), 0xFFFFFFFFU); - // Setup DMA - register_set(&DMA1_Stream1->M0AR, (uint32_t)fake_siren_lut, 0xFFFFFFFFU); - register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); - DMA1_Stream1->NDTR = sizeof(fake_siren_lut); - DMA1_Stream1->CR = (0b11UL << DMA_SxCR_PL_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC | (1U << DMA_SxCR_DIR_Pos); -} - -void fake_siren_codec_enable(bool enabled) { - if (enabled) { - bool success = true; - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2B, (1U << 1)); // Left speaker mix from INA1 - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2C, (1U << 1)); // Right speaker mix from INA1 - success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3D, 0x17, 0b11111); // Left speaker volume - success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3E, 0x17, 0b11111); // Right speaker volume - success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x37, 0b101, 0b111); // INA gain - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7)); // Enable INA - success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x51, (1U << 7)); // Disable global shutdown - if (!success) { - print("Siren codec enable failed\n"); - fault_occurred(FAULT_SIREN_MALFUNCTION); - } - } else { - // Disable INA input. Make sure to retry a few times if the I2C bus is busy. - for (uint8_t i=0U; i<10U; i++) { - if (i2c_clear_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7))) { - break; - } - } - } -} - -static void fake_i2c_siren_init(void) { - siren_dac_init(); - siren_dma_init(); - siren_tim7_init(); - // Enable the I2C to the codec - i2c_init(I2C5); - fake_siren_codec_enable(false); -} - -void fake_i2c_siren_set(bool enabled) { - static bool initialized = false; - static bool fake_siren_enabled = false; - - if (!initialized) { - fake_i2c_siren_init(); - initialized = true; - } - - if (enabled != fake_siren_enabled) { - fake_siren_codec_enable(enabled); - } - - if (enabled) { - register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - } else { - register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - } - fake_siren_enabled = enabled; -} - -void fake_siren_set(bool enabled) { - static bool initialized = false; - static bool fake_siren_enabled = false; - - if (!initialized) { - siren_tim7_init(); - initialized = true; - } - - if (enabled != fake_siren_enabled) { - if (enabled) { - sound_stop_dac(); - siren_dac_init(); - siren_dma_init(); - current_board->set_amp_enabled(true); - register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - } else { - current_board->set_amp_enabled(false); - // Stop modified 8-bit DAC and start normal 12-bit DAC - sound_stop_dac(); - sound_init_dac(); - register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); - } - fake_siren_enabled = enabled; - } -} +void siren_tim7_init(void); +void siren_dac_init(void); +void siren_dma_init(void); +void fake_siren_codec_enable(bool enabled); +void fake_i2c_siren_set(bool enabled); +void fake_siren_set(bool enabled); diff --git a/board/drivers/fan.c b/board/drivers/fan.c new file mode 100644 index 00000000000..2b925cec036 --- /dev/null +++ b/board/drivers/fan.c @@ -0,0 +1,49 @@ +#include "board/config.h" +#include "board/drivers/fan.h" + +struct fan_state_t fan_state; + +static const uint8_t FAN_TICK_FREQ = 8U; + +void fan_set_power(uint8_t percentage) { + if (percentage > 0U) { + fan_state.power = CLAMP(percentage, 20U, 100U); + } else { + fan_state.power = 0U; + } +} + +void fan_init(void) { + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + llfan_init(); +} + +// Call this at FAN_TICK_FREQ +void fan_tick(void) { + if (current_board->has_fan) { + // Measure fan RPM + uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation + fan_state.tach_counter = 0U; + fan_state.rpm = (fan_rpm_fast + (3U * fan_state.rpm)) / 4U; + + #ifdef DEBUG_FAN + puth(fan_state.target_rpm); + print(" "); puth(fan_rpm_fast); + print(" "); puth(fan_state.power); + print("\n"); + #endif + + // Cooldown counter to prevent noise on tachometer line. + if (fan_state.power > 0U) { + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + } else { + if (fan_state.cooldown_counter > 0U) { + fan_state.cooldown_counter--; + } + } + + // Set PWM and enable line + pwm_set(TIM3, 3, fan_state.power); + current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U)); + } +} diff --git a/board/drivers/fan.h b/board/drivers/fan.h index 389fdd3f409..8e9fde3ff0b 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -1,48 +1,18 @@ -#include "board/drivers/drivers.h" - -struct fan_state_t fan_state; - -static const uint8_t FAN_TICK_FREQ = 8U; - -void fan_set_power(uint8_t percentage) { - if (percentage > 0U) { - fan_state.power = CLAMP(percentage, 20U, 100U); - } else { - fan_state.power = 0U; - } -} - -void fan_init(void) { - fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; - llfan_init(); -} - +#pragma once + +#include + +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) { - if (current_board->has_fan) { - // Measure fan RPM - uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation - fan_state.tach_counter = 0U; - fan_state.rpm = (fan_rpm_fast + (3U * fan_state.rpm)) / 4U; - - #ifdef DEBUG_FAN - puth(fan_state.target_rpm); - print(" "); puth(fan_rpm_fast); - print(" "); puth(fan_state.power); - print("\n"); - #endif - - // Cooldown counter to prevent noise on tachometer line. - if (fan_state.power > 0U) { - fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; - } else { - if (fan_state.cooldown_counter > 0U) { - fan_state.cooldown_counter--; - } - } - - // Set PWM and enable line - pwm_set(TIM3, 3, fan_state.power); - current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U)); - } -} +void fan_tick(void); diff --git a/board/drivers/fdcan.c b/board/drivers/fdcan.c new file mode 100644 index 00000000000..97263ddc6ff --- /dev/null +++ b/board/drivers/fdcan.c @@ -0,0 +1,280 @@ +#include "board/config.h" +#include "board/drivers/fdcan.h" +#include "board/can_comms.h" + +#ifdef PANDA_BODY +// Forward declare body_can_rx to avoid pulling in bldc.h (which includes .c files) +void body_can_rx(CANPacket_t *msg); +#endif + +FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; + +static bool can_set_speed(uint8_t can_number) { + bool ret = true; + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + ret &= llcan_set_speed( + FDCANx, + bus_config[bus_number].can_speed, + bus_config[bus_number].can_data_speed, + bus_config[bus_number].canfd_non_iso, + can_loopback, + can_silent + ); + return ret; +} + +void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number) { + static uint32_t last_reset = 0U; + uint32_t time = microsecond_timer_get(); + + // Resetting CAN core is a slow blocking operation, limit frequency + if (get_ts_elapsed(time, last_reset) > 100000U) { // 10 Hz + can_health[can_number].can_core_reset_cnt += 1U; + can_health[can_number].total_tx_lost_cnt += (FDCAN_TX_FIFO_EL_CNT - (FDCANx->TXFQS & FDCAN_TXFQS_TFFL)); // TX FIFO msgs will be lost after reset + llcan_clear_send(FDCANx); + last_reset = time; + } +} + +void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { + uint8_t can_irq_number[PANDA_CAN_CNT][2] = { + { FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn }, + { FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn }, + { FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn }, + }; + + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint32_t psr_reg = FDCANx->PSR; + uint32_t ecr_reg = FDCANx->ECR; + + can_health[can_number].bus_off = ((psr_reg & FDCAN_PSR_BO) >> FDCAN_PSR_BO_Pos); + can_health[can_number].bus_off_cnt += can_health[can_number].bus_off; + can_health[can_number].error_warning = ((psr_reg & FDCAN_PSR_EW) >> FDCAN_PSR_EW_Pos); + can_health[can_number].error_passive = ((psr_reg & FDCAN_PSR_EP) >> FDCAN_PSR_EP_Pos); + + can_health[can_number].last_error = ((psr_reg & FDCAN_PSR_LEC) >> FDCAN_PSR_LEC_Pos); + if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) { + can_health[can_number].last_stored_error = can_health[can_number].last_error; + } + + can_health[can_number].last_data_error = ((psr_reg & FDCAN_PSR_DLEC) >> FDCAN_PSR_DLEC_Pos); + if ((can_health[can_number].last_data_error != 0U) && (can_health[can_number].last_data_error != 7U)) { + can_health[can_number].last_data_stored_error = can_health[can_number].last_data_error; + } + + can_health[can_number].receive_error_cnt = ((ecr_reg & FDCAN_ECR_REC) >> FDCAN_ECR_REC_Pos); + can_health[can_number].transmit_error_cnt = ((ecr_reg & FDCAN_ECR_TEC) >> FDCAN_ECR_TEC_Pos); + + can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate; + can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate; + + if (ir_reg != 0U) { + // Clear error interrupts + FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L); + can_health[can_number].total_error_cnt += 1U; + // Check for RX FIFO overflow + if ((ir_reg & (FDCAN_IR_RF0L)) != 0U) { + can_health[can_number].total_rx_lost_cnt += 1U; + } + // Cases: + // 1. while multiplexing between buses 1 and 3 we are getting ACK errors that overwhelm CAN core, by resetting it recovers faster + // 2. H7 gets stuck in bus off recovery state indefinitely + if ((((can_health[can_number].last_error == CAN_ACK_ERROR) || (can_health[can_number].last_data_error == CAN_ACK_ERROR)) && (can_health[can_number].transmit_error_cnt > 127U)) || + ((ir_reg & FDCAN_IR_BO) != 0U)) { + can_clear_send(FDCANx, can_number); + } + } +} + +// ***************************** CAN ***************************** +// FDFDCANx_IT1 IRQ Handler (TX) +void process_can(uint8_t can_number) { + if (can_number != 0xffU) { + ENTER_CRITICAL(); + + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + FDCANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag + + if ((FDCANx->TXFQS & FDCAN_TXFQS_TFQF) == 0U) { + CANPacket_t to_send; + if (can_pop(can_queues[bus_number], &to_send)) { + if (can_check_checksum(&to_send)) { + can_health[can_number].total_tx_cnt += 1U; + + uint32_t TxFIFOSA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET) + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); + // get the index of the next TX FIFO element (0 to FDCAN_TX_FIFO_EL_CNT - 1) + uint32_t tx_index = (FDCANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1FU; + // only send if we have received a packet + canfd_fifo *fifo; + fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); + + fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0U) ? (to_send.addr) : (to_send.addr << 18)); + + // If canfd_auto is set, outgoing packets will be automatically sent as CAN-FD if an incoming CAN-FD packet was seen + bool fd = bus_config[can_number].canfd_auto ? bus_config[can_number].canfd_enabled : (bool)(to_send.fd > 0U); + uint32_t canfd_enabled_header = fd ? (1UL << 21) : 0UL; + + uint32_t brs_enabled_header = bus_config[can_number].brs_enabled ? (1UL << 20) : 0UL; + fifo->header[1] = (to_send.data_len_code << 16) | canfd_enabled_header | brs_enabled_header; + + uint8_t data_len_w = (dlc_to_len[to_send.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_send.data_len_code] % 4U) > 0U) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4U]); + } + + FDCANx->TXBAR = (1UL << tx_index); + + // Send back to USB + CANPacket_t to_push; + + to_push.fd = fd; + to_push.returned = 1U; + to_push.rejected = 0U; + to_push.extended = to_send.extended; + to_push.addr = to_send.addr; + to_push.bus = bus_number; + to_push.data_len_code = to_send.data_len_code; + (void)memcpy(to_push.data, to_send.data, dlc_to_len[to_push.data_len_code]); + can_set_checksum(&to_push); + + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + } else { + can_health[can_number].total_tx_checksum_error_cnt += 1U; + } + + refresh_can_tx_slots_available(); + } + } + EXIT_CRITICAL(); + } +} + +// FDFDCANx_IT0 IRQ Handler (RX and errors) +// blink blue when we are receiving CAN messages +void can_rx(uint8_t can_number) { + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + uint32_t ir_reg = FDCANx->IR; + + // Clear all new messages from Rx FIFO 0 + FDCANx->IR |= FDCAN_IR_RF0N; + while ((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0U) { + can_health[can_number].total_rx_cnt += 1U; + // get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1) + uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3FU); + + // Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet) + if ((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) { + rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U); + can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost + } + + uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); + CANPacket_t to_push; + canfd_fifo *fifo; + + // getting address + fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); + + bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U); + bool brs_frame = ((fifo->header[1] >> 20) & 0x1U); + + to_push.fd = canfd_frame; + to_push.returned = 0U; + to_push.rejected = 0U; + to_push.extended = (fifo->header[0] >> 30) & 0x1U; + to_push.addr = ((to_push.extended != 0U) ? (fifo->header[0] & 0x1FFFFFFFU) : ((fifo->header[0] >> 18) & 0x7FFU)); + to_push.bus = bus_number; + to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU); + + uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_push.data_len_code] % 4U) > 0U) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + WORD_TO_BYTE_ARRAY(&to_push.data[i*4U], fifo->data_word[i]); + } + can_set_checksum(&to_push); + + // forwarding (panda only) + int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr); + if (bus_fwd_num < 0) { + bus_fwd_num = bus_config[can_number].forwarding_bus; + } + if (bus_fwd_num != -1) { + CANPacket_t to_send; + + to_send.fd = to_push.fd; + to_send.returned = 0U; + to_send.rejected = 0U; + to_send.extended = to_push.extended; + to_send.addr = to_push.addr; + to_send.bus = to_push.bus; + to_send.data_len_code = to_push.data_len_code; + (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); + can_set_checksum(&to_send); + + can_send(&to_send, bus_fwd_num, true); + can_health[can_number].total_fwd_cnt += 1U; + } + + #ifdef PANDA_BODY + body_can_rx(&to_push); + #endif + + safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U; + ignition_can_hook(&to_push); + + led_set(LED_BLUE, true); + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + + // Enable CAN FD and BRS if CAN FD message was received + if (!(bus_config[can_number].canfd_enabled) && (canfd_frame)) { + bus_config[can_number].canfd_enabled = true; + } + if (!(bus_config[can_number].brs_enabled) && (brs_frame)) { + bus_config[can_number].brs_enabled = true; + } + + // update read index + FDCANx->RXF0A = rx_fifo_idx; + } + + // Error handling + if ((ir_reg & (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L)) != 0U) { + update_can_health_pkt(can_number, ir_reg); + } +} + +static void FDCAN1_IT0_IRQ_Handler(void) { can_rx(0); } +static void FDCAN1_IT1_IRQ_Handler(void) { process_can(0); } + +static void FDCAN2_IT0_IRQ_Handler(void) { can_rx(1); } +static void FDCAN2_IT1_IRQ_Handler(void) { process_can(1); } + +static void FDCAN3_IT0_IRQ_Handler(void) { can_rx(2); } +static void FDCAN3_IT1_IRQ_Handler(void) { process_can(2); } + +bool can_init(uint8_t can_number) { + bool ret = false; + + REGISTER_INTERRUPT(FDCAN1_IT0_IRQn, FDCAN1_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(FDCAN1_IT1_IRQn, FDCAN1_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(FDCAN2_IT0_IRQn, FDCAN2_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(FDCAN2_IT1_IRQn, FDCAN2_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(FDCAN3_IT0_IRQn, FDCAN3_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + REGISTER_INTERRUPT(FDCAN3_IT1_IRQn, FDCAN3_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + + if (can_number != 0xffU) { + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + ret &= can_set_speed(can_number); + ret &= llcan_init(FDCANx); + // in case there are queued up messages + process_can(can_number); + } + return ret; +} diff --git a/board/drivers/fdcan.h b/board/drivers/fdcan.h index b7fa20bf09e..0a94225b7e0 100644 --- a/board/drivers/fdcan.h +++ b/board/drivers/fdcan.h @@ -1,273 +1,27 @@ -#include "board/drivers/drivers.h" +#pragma once -FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; +#include +#include -static bool can_set_speed(uint8_t can_number) { - bool ret = true; - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); +#include "board/can.h" - ret &= llcan_set_speed( - FDCANx, - bus_config[bus_number].can_speed, - bus_config[bus_number].can_data_speed, - bus_config[bus_number].canfd_non_iso, - can_loopback, - can_silent - ); - return ret; -} +void process_can(uint8_t can_number); +void can_rx(uint8_t can_number); +bool can_init(uint8_t can_number); -void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number) { - static uint32_t last_reset = 0U; - uint32_t time = microsecond_timer_get(); +#ifdef STM32H7 - // Resetting CAN core is a slow blocking operation, limit frequency - if (get_ts_elapsed(time, last_reset) > 100000U) { // 10 Hz - can_health[can_number].can_core_reset_cnt += 1U; - can_health[can_number].total_tx_lost_cnt += (FDCAN_TX_FIFO_EL_CNT - (FDCANx->TXFQS & FDCAN_TXFQS_TFFL)); // TX FIFO msgs will be lost after reset - llcan_clear_send(FDCANx); - last_reset = time; - } -} +typedef struct { + volatile uint32_t header[2]; + volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U]; +} canfd_fifo; -void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { - uint8_t can_irq_number[PANDA_CAN_CNT][2] = { - { FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn }, - { FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn }, - { FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn }, - }; +extern FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT]; - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint32_t psr_reg = FDCANx->PSR; - uint32_t ecr_reg = FDCANx->ECR; +#define CANIF_FROM_CAN_NUM(num) (cans[num]) +#define CAN_ACK_ERROR 3U - can_health[can_number].bus_off = ((psr_reg & FDCAN_PSR_BO) >> FDCAN_PSR_BO_Pos); - can_health[can_number].bus_off_cnt += can_health[can_number].bus_off; - can_health[can_number].error_warning = ((psr_reg & FDCAN_PSR_EW) >> FDCAN_PSR_EW_Pos); - can_health[can_number].error_passive = ((psr_reg & FDCAN_PSR_EP) >> FDCAN_PSR_EP_Pos); +void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number); +void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg); - can_health[can_number].last_error = ((psr_reg & FDCAN_PSR_LEC) >> FDCAN_PSR_LEC_Pos); - if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) { - can_health[can_number].last_stored_error = can_health[can_number].last_error; - } - - can_health[can_number].last_data_error = ((psr_reg & FDCAN_PSR_DLEC) >> FDCAN_PSR_DLEC_Pos); - if ((can_health[can_number].last_data_error != 0U) && (can_health[can_number].last_data_error != 7U)) { - can_health[can_number].last_data_stored_error = can_health[can_number].last_data_error; - } - - can_health[can_number].receive_error_cnt = ((ecr_reg & FDCAN_ECR_REC) >> FDCAN_ECR_REC_Pos); - can_health[can_number].transmit_error_cnt = ((ecr_reg & FDCAN_ECR_TEC) >> FDCAN_ECR_TEC_Pos); - - can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate; - can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate; - - if (ir_reg != 0U) { - // Clear error interrupts - FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L); - can_health[can_number].total_error_cnt += 1U; - // Check for RX FIFO overflow - if ((ir_reg & (FDCAN_IR_RF0L)) != 0U) { - can_health[can_number].total_rx_lost_cnt += 1U; - } - // Cases: - // 1. while multiplexing between buses 1 and 3 we are getting ACK errors that overwhelm CAN core, by resetting it recovers faster - // 2. H7 gets stuck in bus off recovery state indefinitely - if ((((can_health[can_number].last_error == CAN_ACK_ERROR) || (can_health[can_number].last_data_error == CAN_ACK_ERROR)) && (can_health[can_number].transmit_error_cnt > 127U)) || - ((ir_reg & FDCAN_IR_BO) != 0U)) { - can_clear_send(FDCANx, can_number); - } - } -} - -// ***************************** CAN ***************************** -// FDFDCANx_IT1 IRQ Handler (TX) -void process_can(uint8_t can_number) { - if (can_number != 0xffU) { - ENTER_CRITICAL(); - - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); - - FDCANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag - - if ((FDCANx->TXFQS & FDCAN_TXFQS_TFQF) == 0U) { - CANPacket_t to_send; - if (can_pop(can_queues[bus_number], &to_send)) { - if (can_check_checksum(&to_send)) { - can_health[can_number].total_tx_cnt += 1U; - - uint32_t TxFIFOSA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET) + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); - // get the index of the next TX FIFO element (0 to FDCAN_TX_FIFO_EL_CNT - 1) - uint32_t tx_index = (FDCANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1FU; - // only send if we have received a packet - canfd_fifo *fifo; - fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); - - fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0U) ? (to_send.addr) : (to_send.addr << 18)); - - // If canfd_auto is set, outgoing packets will be automatically sent as CAN-FD if an incoming CAN-FD packet was seen - bool fd = bus_config[can_number].canfd_auto ? bus_config[can_number].canfd_enabled : (bool)(to_send.fd > 0U); - uint32_t canfd_enabled_header = fd ? (1UL << 21) : 0UL; - - uint32_t brs_enabled_header = bus_config[can_number].brs_enabled ? (1UL << 20) : 0UL; - fifo->header[1] = (to_send.data_len_code << 16) | canfd_enabled_header | brs_enabled_header; - - uint8_t data_len_w = (dlc_to_len[to_send.data_len_code] / 4U); - data_len_w += ((dlc_to_len[to_send.data_len_code] % 4U) > 0U) ? 1U : 0U; - for (unsigned int i = 0; i < data_len_w; i++) { - BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4U]); - } - - FDCANx->TXBAR = (1UL << tx_index); - - // Send back to USB - CANPacket_t to_push; - - to_push.fd = fd; - to_push.returned = 1U; - to_push.rejected = 0U; - to_push.extended = to_send.extended; - to_push.addr = to_send.addr; - to_push.bus = bus_number; - to_push.data_len_code = to_send.data_len_code; - (void)memcpy(to_push.data, to_send.data, dlc_to_len[to_push.data_len_code]); - can_set_checksum(&to_push); - - rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; - } else { - can_health[can_number].total_tx_checksum_error_cnt += 1U; - } - - refresh_can_tx_slots_available(); - } - } - EXIT_CRITICAL(); - } -} - -// FDFDCANx_IT0 IRQ Handler (RX and errors) -// blink blue when we are receiving CAN messages -void can_rx(uint8_t can_number) { - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); - - uint32_t ir_reg = FDCANx->IR; - - // Clear all new messages from Rx FIFO 0 - FDCANx->IR |= FDCAN_IR_RF0N; - while ((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0U) { - can_health[can_number].total_rx_cnt += 1U; - // get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1) - uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3FU); - - // Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet) - if ((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) { - rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U); - can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost - } - - uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); - CANPacket_t to_push; - canfd_fifo *fifo; - - // getting address - fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); - - bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U); - bool brs_frame = ((fifo->header[1] >> 20) & 0x1U); - - to_push.fd = canfd_frame; - to_push.returned = 0U; - to_push.rejected = 0U; - to_push.extended = (fifo->header[0] >> 30) & 0x1U; - to_push.addr = ((to_push.extended != 0U) ? (fifo->header[0] & 0x1FFFFFFFU) : ((fifo->header[0] >> 18) & 0x7FFU)); - to_push.bus = bus_number; - to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU); - - uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U); - data_len_w += ((dlc_to_len[to_push.data_len_code] % 4U) > 0U) ? 1U : 0U; - for (unsigned int i = 0; i < data_len_w; i++) { - WORD_TO_BYTE_ARRAY(&to_push.data[i*4U], fifo->data_word[i]); - } - can_set_checksum(&to_push); - - // forwarding (panda only) - int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr); - if (bus_fwd_num < 0) { - bus_fwd_num = bus_config[can_number].forwarding_bus; - } - if (bus_fwd_num != -1) { - CANPacket_t to_send; - - to_send.fd = to_push.fd; - to_send.returned = 0U; - to_send.rejected = 0U; - to_send.extended = to_push.extended; - to_send.addr = to_push.addr; - to_send.bus = to_push.bus; - to_send.data_len_code = to_push.data_len_code; - (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); - can_set_checksum(&to_send); - - can_send(&to_send, bus_fwd_num, true); - can_health[can_number].total_fwd_cnt += 1U; - } - - #ifdef PANDA_BODY - body_can_rx(&to_push); - #endif - - safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U; - ignition_can_hook(&to_push); - - led_set(LED_BLUE, true); - rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; - - // Enable CAN FD and BRS if CAN FD message was received - if (!(bus_config[can_number].canfd_enabled) && (canfd_frame)) { - bus_config[can_number].canfd_enabled = true; - } - if (!(bus_config[can_number].brs_enabled) && (brs_frame)) { - bus_config[can_number].brs_enabled = true; - } - - // update read index - FDCANx->RXF0A = rx_fifo_idx; - } - - // Error handling - if ((ir_reg & (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L)) != 0U) { - update_can_health_pkt(can_number, ir_reg); - } -} - -static void FDCAN1_IT0_IRQ_Handler(void) { can_rx(0); } -static void FDCAN1_IT1_IRQ_Handler(void) { process_can(0); } - -static void FDCAN2_IT0_IRQ_Handler(void) { can_rx(1); } -static void FDCAN2_IT1_IRQ_Handler(void) { process_can(1); } - -static void FDCAN3_IT0_IRQ_Handler(void) { can_rx(2); } -static void FDCAN3_IT1_IRQ_Handler(void) { process_can(2); } - -bool can_init(uint8_t can_number) { - bool ret = false; - - REGISTER_INTERRUPT(FDCAN1_IT0_IRQn, FDCAN1_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) - REGISTER_INTERRUPT(FDCAN1_IT1_IRQn, FDCAN1_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) - REGISTER_INTERRUPT(FDCAN2_IT0_IRQn, FDCAN2_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) - REGISTER_INTERRUPT(FDCAN2_IT1_IRQn, FDCAN2_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) - REGISTER_INTERRUPT(FDCAN3_IT0_IRQn, FDCAN3_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) - REGISTER_INTERRUPT(FDCAN3_IT1_IRQn, FDCAN3_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) - - if (can_number != 0xffU) { - FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); - ret &= can_set_speed(can_number); - ret &= llcan_init(FDCANx); - // in case there are queued up messages - process_can(can_number); - } - return ret; -} +#endif // STM32H7 diff --git a/board/drivers/gpio.c b/board/drivers/gpio.c new file mode 100644 index 00000000000..e48cdc927c9 --- /dev/null +++ b/board/drivers/gpio.c @@ -0,0 +1,81 @@ +#include "board/config.h" +#include "board/drivers/gpio.h" + +// Detection with internal pullup +#define PULL_EFFECTIVE_DELAY 4096 + +void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->MODER; + tmp &= ~(3U << (pin * 2U)); + tmp |= (mode << (pin * 2U)); + register_set(&(GPIO->MODER), tmp, 0xFFFFFFFFU); + EXIT_CRITICAL(); +} + +void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { + ENTER_CRITICAL(); + if (enabled) { + register_set_bits(&(GPIO->ODR), (1UL << pin)); + } else { + register_clear_bits(&(GPIO->ODR), (1UL << pin)); + } + set_gpio_mode(GPIO, pin, MODE_OUTPUT); + EXIT_CRITICAL(); +} + +void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){ + ENTER_CRITICAL(); + if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { + register_set_bits(&(GPIO->OTYPER), (1UL << pin)); + } else { + register_clear_bits(&(GPIO->OTYPER), (1U << pin)); + } + EXIT_CRITICAL(); +} + +void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->AFR[pin >> 3U]; + tmp &= ~(0xFU << ((pin & 7U) * 4U)); + tmp |= mode << ((pin & 7U) * 4U); + register_set(&(GPIO->AFR[pin >> 3]), tmp, 0xFFFFFFFFU); + set_gpio_mode(GPIO, pin, MODE_ALTERNATE); + EXIT_CRITICAL(); +} + +void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->PUPDR; + tmp &= ~(3U << (pin * 2U)); + tmp |= (mode << (pin * 2U)); + register_set(&(GPIO->PUPDR), tmp, 0xFFFFFFFFU); + EXIT_CRITICAL(); +} + +int get_gpio_input(const GPIO_TypeDef *GPIO, unsigned int pin) { + return (GPIO->IDR & (1UL << pin)) == (1UL << pin); +} + +#ifdef PANDA_JUNGLE +void gpio_set_all_output(gpio_t *pins, uint8_t num_pins, bool enabled) { + for (uint8_t i = 0; i < num_pins; i++) { + set_gpio_output(pins[i].bank, pins[i].pin, enabled); + } +} + +void gpio_set_bitmask(gpio_t *pins, uint8_t num_pins, uint32_t bitmask) { + for (uint8_t i = 0; i < num_pins; i++) { + set_gpio_output(pins[i].bank, pins[i].pin, (bitmask >> i) & 1U); + } +} +#endif + +bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { + set_gpio_mode(GPIO, pin, MODE_INPUT); + set_gpio_pullup(GPIO, pin, mode); + for (volatile int i=0; i + #define MODE_INPUT 0 #define MODE_OUTPUT 1 #define MODE_ALTERNATE 2 @@ -10,58 +14,13 @@ #define OUTPUT_TYPE_PUSH_PULL 0U #define OUTPUT_TYPE_OPEN_DRAIN 1U -void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { - ENTER_CRITICAL(); - uint32_t tmp = GPIO->MODER; - tmp &= ~(3U << (pin * 2U)); - tmp |= (mode << (pin * 2U)); - register_set(&(GPIO->MODER), tmp, 0xFFFFFFFFU); - EXIT_CRITICAL(); -} - -void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { - ENTER_CRITICAL(); - if (enabled) { - register_set_bits(&(GPIO->ODR), (1UL << pin)); - } else { - register_clear_bits(&(GPIO->ODR), (1UL << pin)); - } - set_gpio_mode(GPIO, pin, MODE_OUTPUT); - EXIT_CRITICAL(); -} - -void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){ - ENTER_CRITICAL(); - if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { - register_set_bits(&(GPIO->OTYPER), (1UL << pin)); - } else { - register_clear_bits(&(GPIO->OTYPER), (1U << pin)); - } - EXIT_CRITICAL(); -} - -void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { - ENTER_CRITICAL(); - uint32_t tmp = GPIO->AFR[pin >> 3U]; - tmp &= ~(0xFU << ((pin & 7U) * 4U)); - tmp |= mode << ((pin & 7U) * 4U); - register_set(&(GPIO->AFR[pin >> 3]), tmp, 0xFFFFFFFFU); - set_gpio_mode(GPIO, pin, MODE_ALTERNATE); - EXIT_CRITICAL(); -} - -void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { - ENTER_CRITICAL(); - uint32_t tmp = GPIO->PUPDR; - tmp &= ~(3U << (pin * 2U)); - tmp |= (mode << (pin * 2U)); - register_set(&(GPIO->PUPDR), tmp, 0xFFFFFFFFU); - EXIT_CRITICAL(); -} - -int get_gpio_input(const GPIO_TypeDef *GPIO, unsigned int pin) { - return (GPIO->IDR & (1UL << pin)) == (1UL << pin); -} +void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode); +void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled); +void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type); +void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode); +void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode); +int get_gpio_input(const GPIO_TypeDef *GPIO, unsigned int pin); +bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode); #ifdef PANDA_JUNGLE typedef struct { @@ -69,26 +28,6 @@ typedef struct { uint8_t pin; } gpio_t; -void gpio_set_all_output(gpio_t *pins, uint8_t num_pins, bool enabled) { - for (uint8_t i = 0; i < num_pins; i++) { - set_gpio_output(pins[i].bank, pins[i].pin, enabled); - } -} - -void gpio_set_bitmask(gpio_t *pins, uint8_t num_pins, uint32_t bitmask) { - for (uint8_t i = 0; i < num_pins; i++) { - set_gpio_output(pins[i].bank, pins[i].pin, (bitmask >> i) & 1U); - } -} +void gpio_set_all_output(gpio_t *pins, uint8_t num_pins, bool enabled); +void gpio_set_bitmask(gpio_t *pins, uint8_t num_pins, uint32_t bitmask); #endif - -// Detection with internal pullup -#define PULL_EFFECTIVE_DELAY 4096 -bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { - set_gpio_mode(GPIO, pin, MODE_INPUT); - set_gpio_pullup(GPIO, pin, mode); - for (volatile int i=0; iharness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay); + } else { + set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay); + } + + if (!(drive_relay || ignition_relay)) { + harness.relay_driven = false; + } +} + +bool harness_check_ignition(void) { + bool ret = false; + + // wait until we're not reading the analog voltages anymore + while (harness.sbu_adc_lock) {} + + switch(harness.status){ + case HARNESS_STATUS_NORMAL: + ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1); + break; + case HARNESS_STATUS_FLIPPED: + ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2); + break; + default: + break; + } + return ret; +} + +static uint8_t harness_detect_orientation(void) { + uint8_t ret = harness.status; + + #ifndef BOOTSTUB + // We can't detect orientation if the relay is being driven + if (!harness.relay_driven) { + harness.sbu_adc_lock = true; + set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_ANALOG); + set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_ANALOG); + + harness.sbu1_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU1); + harness.sbu2_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU2); + uint16_t detection_threshold = current_board->avdd_mV / 2U; + + // Detect connection and orientation + if((harness.sbu1_voltage_mV < detection_threshold) || (harness.sbu2_voltage_mV < detection_threshold)){ + if (harness.sbu1_voltage_mV < harness.sbu2_voltage_mV) { + // orientation flipped (PANDA_SBU1->HARNESS_SBU1(relay), PANDA_SBU2->HARNESS_SBU2(ign)) + ret = HARNESS_STATUS_FLIPPED; + } else { + // orientation normal (PANDA_SBU2->HARNESS_SBU1(relay), PANDA_SBU1->HARNESS_SBU2(ign)) + // (SBU1->SBU2 is the normal orientation connection per USB-C cable spec) + ret = HARNESS_STATUS_NORMAL; + } + } else { + ret = HARNESS_STATUS_NC; + } + + // Pins are not 5V tolerant in ADC mode + 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); + harness.sbu_adc_lock = false; + } + #endif + + return ret; +} + +void harness_tick(void) { + harness.status = harness_detect_orientation(); +} + +void harness_init(void) { + // init OBD_SBUx_RELAY + set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, 1); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, 1); + + // detect initial orientation + harness.status = harness_detect_orientation(); + + // keep buses connected by default + set_intercept_relay(false, false); +} diff --git a/board/drivers/harness.h b/board/drivers/harness.h index d501ab949fd..d70fc4d7e13 100644 --- a/board/drivers/harness.h +++ b/board/drivers/harness.h @@ -1,106 +1,40 @@ -#include "board/drivers/drivers.h" - -struct harness_t harness; +#pragma once + +#include +#include + +#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; + +#ifdef STM32H7 +#include "board/stm32h7/lladc.h" + +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; +}; +#endif // STM32H7 // The ignition relay is only used for testing purposes -void set_intercept_relay(bool intercept, bool ignition_relay) { - bool drive_relay = intercept; - if (harness.status == HARNESS_STATUS_NC) { - // no harness, no relay to drive - drive_relay = false; - } - - if (drive_relay || ignition_relay) { - harness.relay_driven = true; - } - - // wait until we're not reading the analog voltages anymore - while (harness.sbu_adc_lock) {} - - if (harness.status == HARNESS_STATUS_NORMAL) { - set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay); - } else { - set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay); - } - - if (!(drive_relay || ignition_relay)) { - harness.relay_driven = false; - } -} - -bool harness_check_ignition(void) { - bool ret = false; - - // wait until we're not reading the analog voltages anymore - while (harness.sbu_adc_lock) {} - - switch(harness.status){ - case HARNESS_STATUS_NORMAL: - ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1); - break; - case HARNESS_STATUS_FLIPPED: - ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2); - break; - default: - break; - } - return ret; -} - -static uint8_t harness_detect_orientation(void) { - uint8_t ret = harness.status; - - #ifndef BOOTSTUB - // We can't detect orientation if the relay is being driven - if (!harness.relay_driven) { - harness.sbu_adc_lock = true; - set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_ANALOG); - set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_ANALOG); - - harness.sbu1_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU1); - harness.sbu2_voltage_mV = adc_get_mV(¤t_board->harness_config->adc_signal_SBU2); - uint16_t detection_threshold = current_board->avdd_mV / 2U; - - // Detect connection and orientation - if((harness.sbu1_voltage_mV < detection_threshold) || (harness.sbu2_voltage_mV < detection_threshold)){ - if (harness.sbu1_voltage_mV < harness.sbu2_voltage_mV) { - // orientation flipped (PANDA_SBU1->HARNESS_SBU1(relay), PANDA_SBU2->HARNESS_SBU2(ign)) - ret = HARNESS_STATUS_FLIPPED; - } else { - // orientation normal (PANDA_SBU2->HARNESS_SBU1(relay), PANDA_SBU1->HARNESS_SBU2(ign)) - // (SBU1->SBU2 is the normal orientation connection per USB-C cable spec) - ret = HARNESS_STATUS_NORMAL; - } - } else { - ret = HARNESS_STATUS_NC; - } - - // Pins are not 5V tolerant in ADC mode - 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); - harness.sbu_adc_lock = false; - } - #endif - - return ret; -} - -void harness_tick(void) { - harness.status = harness_detect_orientation(); -} - -void harness_init(void) { - // init OBD_SBUx_RELAY - set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, OUTPUT_TYPE_OPEN_DRAIN); - set_gpio_output_type(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, OUTPUT_TYPE_OPEN_DRAIN); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, 1); - set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, 1); - - // detect initial orientation - harness.status = harness_detect_orientation(); - - // keep buses connected by default - set_intercept_relay(false, false); -} +void set_intercept_relay(bool intercept, bool ignition_relay); +bool harness_check_ignition(void); +void harness_tick(void); +void harness_init(void); diff --git a/board/drivers/interrupts.c b/board/drivers/interrupts.c new file mode 100644 index 00000000000..eb96600fa45 --- /dev/null +++ b/board/drivers/interrupts.c @@ -0,0 +1,82 @@ +#include "board/config.h" +#include "board/drivers/interrupts.h" + +void unused_interrupt_handler(void) { + // Something is wrong if this handler is called! + print("Unused interrupt handler called!\n"); + fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED); +} + +interrupt interrupts[NUM_INTERRUPTS]; + +static bool check_interrupt_rate = false; + +static uint32_t idle_time = 0U; +static uint32_t busy_time = 0U; +float interrupt_load = 0.0f; + +void handle_interrupt(IRQn_Type irq_type){ + static uint8_t interrupt_depth = 0U; + static uint32_t last_time = 0U; + ENTER_CRITICAL(); + if (interrupt_depth == 0U) { + uint32_t time = microsecond_timer_get(); + idle_time += get_ts_elapsed(time, last_time); + last_time = time; + } + interrupt_depth += 1U; + EXIT_CRITICAL(); + + interrupts[irq_type].call_counter++; + interrupts[irq_type].handler(); + + // Check that the interrupts don't fire too often + if (check_interrupt_rate && (interrupts[irq_type].call_counter > interrupts[irq_type].max_call_rate)) { + fault_occurred(interrupts[irq_type].call_rate_fault); + } + + ENTER_CRITICAL(); + interrupt_depth -= 1U; + if (interrupt_depth == 0U) { + uint32_t time = microsecond_timer_get(); + busy_time += get_ts_elapsed(time, last_time); + last_time = time; + } + EXIT_CRITICAL(); +} + +// Every second +void interrupt_timer_handler(void) { + if (INTERRUPT_TIMER->SR != 0U) { + for (uint16_t i = 0U; i < NUM_INTERRUPTS; i++) { + // Log IRQ call rate faults + if (check_interrupt_rate && (interrupts[i].call_counter > interrupts[i].max_call_rate)) { + print("Interrupt 0x"); puth(i); print(" fired too often (0x"); puth(interrupts[i].call_counter); print("/s)!\n"); + } + + // Reset interrupt counters + interrupts[i].call_rate = interrupts[i].call_counter; + interrupts[i].call_counter = 0U; + } + + // Calculate interrupt load + // The bootstub does not have the FPU enabled, so can't do float operations. +#if !defined(BOOTSTUB) + interrupt_load = ((busy_time + idle_time) > 0U) ? ((float) (((float) busy_time) / (busy_time + idle_time))) : 0.0f; +#endif + idle_time = 0U; + busy_time = 0U; + } + INTERRUPT_TIMER->SR = 0; +} + +void init_interrupts(bool check_rate_limit){ + check_interrupt_rate = check_rate_limit; + + for(uint16_t i=0U; i +#include -interrupt interrupts[NUM_INTERRUPTS]; +#ifdef STM32H7 -static bool check_interrupt_rate = false; +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; -static uint32_t idle_time = 0U; -static uint32_t busy_time = 0U; -float interrupt_load = 0.0f; +void unused_interrupt_handler(void); -void handle_interrupt(IRQn_Type irq_type){ - static uint8_t interrupt_depth = 0U; - static uint32_t last_time = 0U; - ENTER_CRITICAL(); - if (interrupt_depth == 0U) { - uint32_t time = microsecond_timer_get(); - idle_time += get_ts_elapsed(time, last_time); - last_time = time; - } - interrupt_depth += 1U; - EXIT_CRITICAL(); +extern interrupt interrupts[NUM_INTERRUPTS]; - interrupts[irq_type].call_counter++; - interrupts[irq_type].handler(); +#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); - // Check that the interrupts don't fire too often - if (check_interrupt_rate && (interrupts[irq_type].call_counter > interrupts[irq_type].max_call_rate)) { - fault_occurred(interrupts[irq_type].call_rate_fault); - } - - ENTER_CRITICAL(); - interrupt_depth -= 1U; - if (interrupt_depth == 0U) { - uint32_t time = microsecond_timer_get(); - busy_time += get_ts_elapsed(time, last_time); - last_time = time; - } - EXIT_CRITICAL(); -} +extern float interrupt_load; +void handle_interrupt(IRQn_Type irq_type); // Every second -void interrupt_timer_handler(void) { - if (INTERRUPT_TIMER->SR != 0U) { - for (uint16_t i = 0U; i < NUM_INTERRUPTS; i++) { - // Log IRQ call rate faults - if (check_interrupt_rate && (interrupts[i].call_counter > interrupts[i].max_call_rate)) { - print("Interrupt 0x"); puth(i); print(" fired too often (0x"); puth(interrupts[i].call_counter); print("/s)!\n"); - } - - // Reset interrupt counters - interrupts[i].call_rate = interrupts[i].call_counter; - interrupts[i].call_counter = 0U; - } - - // Calculate interrupt load - // The bootstub does not have the FPU enabled, so can't do float operations. -#if !defined(BOOTSTUB) - interrupt_load = ((busy_time + idle_time) > 0U) ? ((float) (((float) busy_time) / (busy_time + idle_time))) : 0.0f; -#endif - idle_time = 0U; - busy_time = 0U; - } - INTERRUPT_TIMER->SR = 0; -} - -void init_interrupts(bool check_rate_limit){ - check_interrupt_rate = check_rate_limit; - - for(uint16_t i=0U; iled_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_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/drivers/led.h b/board/drivers/led.h index 7cea94e854f..c8cb45bef56 100644 --- a/board/drivers/led.h +++ b/board/drivers/led.h @@ -1,3 +1,7 @@ +#pragma once + +#include +#include #define LED_RED 0U #define LED_GREEN 1U @@ -5,28 +9,5 @@ #define LED_PWM_POWER 2U -void led_set(uint8_t color, bool enabled) { - if (color < 3U) { - 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_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); - } -} +void led_set(uint8_t color, bool enabled); +void led_init(void); diff --git a/board/drivers/pwm.c b/board/drivers/pwm.c new file mode 100644 index 00000000000..5c17c0cce8e --- /dev/null +++ b/board/drivers/pwm.c @@ -0,0 +1,55 @@ +#include "board/config.h" +#include "board/drivers/pwm.h" + +void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ + // Enable timer and auto-reload + register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); + + // Set channel as PWM mode 1 and enable output + switch(channel){ + case 1U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); + break; + case 2U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); + break; + case 3U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); + break; + case 4U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); + break; + default: + break; + } + + // Set max counter value + register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); + + // Update registers and clear counter + TIM->EGR |= TIM_EGR_UG; +} + +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ + uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); + switch(channel){ + case 1U: + register_set(&(TIM->CCR1), comp_value, 0xFFFFU); + break; + case 2U: + register_set(&(TIM->CCR2), comp_value, 0xFFFFU); + break; + case 3U: + register_set(&(TIM->CCR3), comp_value, 0xFFFFU); + break; + case 4U: + register_set(&(TIM->CCR4), comp_value, 0xFFFFU); + break; + default: + break; + } +} diff --git a/board/drivers/pwm.h b/board/drivers/pwm.h index 3d0d73efdc6..ffb56bfd377 100644 --- a/board/drivers/pwm.h +++ b/board/drivers/pwm.h @@ -1,56 +1,10 @@ -#define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz - -// TODO: Implement for 32-bit timers +#pragma once -void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ - // Enable timer and auto-reload - register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); +#include - // Set channel as PWM mode 1 and enable output - switch(channel){ - case 1U: - register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); - break; - case 2U: - register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); - break; - case 3U: - register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); - break; - case 4U: - register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); - break; - default: - break; - } - - // Set max counter value - register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); +#define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz - // Update registers and clear counter - TIM->EGR |= TIM_EGR_UG; -} +// TODO: Implement for 32-bit timers -void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ - uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); - switch(channel){ - case 1U: - register_set(&(TIM->CCR1), comp_value, 0xFFFFU); - break; - case 2U: - register_set(&(TIM->CCR2), comp_value, 0xFFFFU); - break; - case 3U: - register_set(&(TIM->CCR3), comp_value, 0xFFFFU); - break; - case 4U: - register_set(&(TIM->CCR4), comp_value, 0xFFFFU); - break; - default: - break; - } -} +void pwm_init(TIM_TypeDef *TIM, uint8_t channel); +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage); diff --git a/board/drivers/registers.c b/board/drivers/registers.c new file mode 100644 index 00000000000..94b5aac2eb9 --- /dev/null +++ b/board/drivers/registers.c @@ -0,0 +1,78 @@ +#include "board/config.h" +#include "board/drivers/registers.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]; + +// Hash spread in first and second iterations seems to be reasonable. +// See: tests/development/register_hashmap_spread.py +// Also, check the collision warnings in the debug output, and minimize those. +static uint16_t hash_addr(uint32_t input){ + return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); +} + +// 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){ + ENTER_CRITICAL() + // Set bits in register that are also in the mask + (*addr) = ((*addr) & (~mask)) | (val & mask); + + // Add these values to the map + uint16_t hash = hash_addr((uint32_t) addr); + uint16_t tries = REGISTER_MAP_SIZE; + while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} + if (tries != 0U){ + register_map[hash].address = addr; + register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); + register_map[hash].check_mask |= mask; + } else { + #ifdef DEBUG_FAULTS + print("Hash collision: address 0x"); puth((uint32_t) addr); print("!\n"); + #endif + } + EXIT_CRITICAL() +} + +// 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) { + register_set(addr, val, 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) { + register_set(addr, (~val), val); +} + +// To be called periodically +void check_registers(void){ + for(uint16_t i=0U; i -#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr))) - -static reg register_map[REGISTER_MAP_SIZE]; - -// Hash spread in first and second iterations seems to be reasonable. -// See: tests/development/register_hashmap_spread.py -// Also, check the collision warnings in the debug output, and minimize those. -static uint16_t hash_addr(uint32_t input){ - return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); -} +// 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){ - ENTER_CRITICAL() - // Set bits in register that are also in the mask - (*addr) = ((*addr) & (~mask)) | (val & mask); - - // Add these values to the map - uint16_t hash = hash_addr((uint32_t) addr); - uint16_t tries = REGISTER_MAP_SIZE; - while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} - if (tries != 0U){ - register_map[hash].address = addr; - register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); - register_map[hash].check_mask |= mask; - } else { - #ifdef DEBUG_FAULTS - print("Hash collision: address 0x"); puth((uint32_t) addr); print("!\n"); - #endif - } - EXIT_CRITICAL() -} - +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) { - register_set(addr, val, val); -} - +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) { - register_set(addr, (~val), val); -} - +void register_clear_bits(volatile uint32_t *addr, uint32_t val); // To be called periodically -void check_registers(void){ - for(uint16_t i=0U; i wd_state.threshold) { + print("WD timeout 0x"); puth(et); print("\n"); + fault_occurred(wd_state.fault); + } + + wd_state.last_ts = ts; +} + +void simple_watchdog_init(uint32_t fault, uint32_t threshold) { + wd_state.fault = fault; + wd_state.threshold = threshold; + wd_state.last_ts = microsecond_timer_get(); +} diff --git a/board/drivers/simple_watchdog.h b/board/drivers/simple_watchdog.h index 7668402fec4..299cdc3e55a 100644 --- a/board/drivers/simple_watchdog.h +++ b/board/drivers/simple_watchdog.h @@ -1,21 +1,12 @@ -#include "board/drivers/drivers.h" +#pragma once -static simple_watchdog_state_t wd_state; +#include -void simple_watchdog_kick(void) { - uint32_t ts = microsecond_timer_get(); +typedef struct simple_watchdog_state_t { + uint32_t fault; + uint32_t last_ts; + uint32_t threshold; +} simple_watchdog_state_t; - uint32_t et = get_ts_elapsed(ts, wd_state.last_ts); - if (et > wd_state.threshold) { - print("WD timeout 0x"); puth(et); print("\n"); - fault_occurred(wd_state.fault); - } - - wd_state.last_ts = ts; -} - -void simple_watchdog_init(uint32_t fault, uint32_t threshold) { - wd_state.fault = fault; - wd_state.threshold = threshold; - wd_state.last_ts = microsecond_timer_get(); -} +void simple_watchdog_kick(void); +void simple_watchdog_init(uint32_t fault, uint32_t threshold); diff --git a/board/drivers/spi.c b/board/drivers/spi.c new file mode 100644 index 00000000000..f614c5b9f8d --- /dev/null +++ b/board/drivers/spi.c @@ -0,0 +1,233 @@ +#include "board/config.h" +#include "board/drivers/spi.h" +#include "board/can_comms.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 + +uint16_t spi_error_count = 0; + +static uint8_t spi_state = SPI_STATE_HEADER; +static uint16_t spi_data_len_mosi; +static bool spi_can_tx_ready = false; +static const unsigned char version_text[] = "VERSION"; + +static uint16_t spi_version_packet(uint8_t *out) { + // this protocol version request is a stable portion of + // the panda's SPI protocol. its contents match that of the + // panda USB descriptors and are sufficent to list/enumerate + // a panda, determine panda type, and bootstub status. + // the response is: + // VERSION + 2 byte data length + data + CRC8 + // echo "VERSION" + (void)memcpy(out, version_text, 7); + + // write response + uint16_t data_len = 0; + uint16_t data_pos = 7U + 2U; + + // write serial + (void)memcpy(&out[data_pos], ((uint8_t *)UID_BASE), 12); + data_len += 12U; + + // HW type + out[data_pos + data_len] = hw_type; + data_len += 1U; + + // bootstub + out[data_pos + data_len] = USB_PID & 0xFFU; + data_len += 1U; + + // SPI protocol version + out[data_pos + data_len] = 0x2; + data_len += 1U; + + // data length + out[7] = data_len & 0xFFU; + out[8] = (data_len >> 8) & 0xFFU; + + // CRC8 + uint16_t resp_len = data_pos + data_len; + out[resp_len] = crc_checksum(out, resp_len, 0xD5U); + resp_len += 1U; + + return resp_len; +} + +void spi_init(void) { + // platform init + llspi_init(); + + // Start the first packet! + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); +} + +static bool validate_checksum(const uint8_t *data, uint16_t len) { + // TODO: can speed this up by casting the bulk to uint32_t and xor-ing the bytes afterwards + uint8_t checksum = SPI_CHECKSUM_START; + for(uint16_t i = 0U; i < len; i++){ + checksum ^= data[i]; + } + return checksum == 0U; +} + +void spi_rx_done(void) { + uint16_t response_len = 0U; + uint8_t next_rx_state = SPI_STATE_HEADER_NACK; + bool checksum_valid = false; + static uint8_t spi_endpoint; + static uint16_t spi_data_len_miso; + + // parse header + spi_endpoint = spi_buf_rx[1]; + spi_data_len_mosi = (spi_buf_rx[3] << 8) | spi_buf_rx[2]; + spi_data_len_miso = (spi_buf_rx[5] << 8) | spi_buf_rx[4]; + + if (memcmp(spi_buf_rx, version_text, 7) == 0) { + response_len = spi_version_packet(spi_buf_tx); + next_rx_state = SPI_STATE_HEADER_NACK;; + } else if (spi_state == SPI_STATE_HEADER) { + checksum_valid = validate_checksum(spi_buf_rx, SPI_HEADER_SIZE); + if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && checksum_valid) { + // response: ACK and start receiving data portion + spi_buf_tx[0] = SPI_HACK; + next_rx_state = SPI_STATE_HEADER_ACK; + response_len = 1U; + } else { + // response: NACK and reset state machine + #ifdef DEBUG_SPI + print("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE); + #endif + spi_buf_tx[0] = SPI_NACK; + next_rx_state = SPI_STATE_HEADER_NACK; + response_len = 1U; + } + } else if (spi_state == SPI_STATE_DATA_RX) { + // We got everything! Based on the endpoint specified, call the appropriate handler + bool response_ack = false; + checksum_valid = validate_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U); + if (checksum_valid) { + if (spi_endpoint == 0U) { + if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { + ControlPacket_t ctrl = {0}; + (void)memcpy((uint8_t*)&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t)); + response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]); + response_ack = true; + } else { + print("SPI: insufficient data for control handler\n"); + } + } else if ((spi_endpoint == 1U) || (spi_endpoint == 0x81U)) { + if (spi_data_len_mosi == 0U) { + response_len = comms_can_read(&(spi_buf_tx[3]), spi_data_len_miso); + response_ack = true; + } else { + print("SPI: did not expect data for can_read\n"); + } + } else if (spi_endpoint == 2U) { + comms_endpoint2_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + response_ack = true; + } else if (spi_endpoint == 3U) { + if (spi_data_len_mosi > 0U) { + if (spi_can_tx_ready) { + spi_can_tx_ready = false; + comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + response_ack = true; + } else { + response_ack = false; + print("SPI: CAN NACK\n"); + } + } else { + print("SPI: did expect data for can_write\n"); + } + } else if (spi_endpoint == 0xABU) { + // test endpoint: mimics panda -> device transfer + response_len = spi_data_len_miso; + response_ack = true; + } else if (spi_endpoint == 0xACU) { + // test endpoint: mimics device -> panda transfer (with NACK) + response_ack = false; + } else { + print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); + } + } else { + // Checksum was incorrect + response_ack = false; + #ifdef DEBUG_SPI + print("- incorrect data checksum "); + puth4(spi_data_len_mosi); + print("\n"); + hexdump(spi_buf_rx, SPI_HEADER_SIZE); + hexdump(&(spi_buf_rx[SPI_HEADER_SIZE]), MIN(spi_data_len_mosi, 64)); + print("\n"); + #endif + } + + if (!response_ack) { + spi_buf_tx[0] = SPI_NACK; + next_rx_state = SPI_STATE_HEADER_NACK; + response_len = 1U; + } else { + // Setup response header + spi_buf_tx[0] = SPI_DACK; + spi_buf_tx[1] = response_len & 0xFFU; + spi_buf_tx[2] = (response_len >> 8) & 0xFFU; + + // Add checksum + uint8_t checksum = SPI_CHECKSUM_START; + for(uint16_t i = 0U; i < (response_len + 3U); i++) { + checksum ^= spi_buf_tx[i]; + } + spi_buf_tx[response_len + 3U] = checksum; + response_len += 4U; + + next_rx_state = SPI_STATE_DATA_TX; + } + } else { + print("SPI: RX unexpected state: "); puth(spi_state); print("\n"); + } + + // send out response + if (response_len == 0U) { + print("SPI: no response\n"); + spi_buf_tx[0] = SPI_NACK; + spi_state = SPI_STATE_HEADER_NACK; + response_len = 1U; + } + llspi_miso_dma(spi_buf_tx, response_len); + + spi_state = next_rx_state; + if (!checksum_valid) { + spi_error_count += 1U; + } +} + +void spi_tx_done(bool reset) { + if ((spi_state == SPI_STATE_HEADER_NACK) || reset) { + // Reset state + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + } else if (spi_state == SPI_STATE_HEADER_ACK) { + // ACK was sent, queue up the RX buf for the data + checksum + spi_state = SPI_STATE_DATA_RX; + llspi_mosi_dma(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi + 1U); + } else if (spi_state == SPI_STATE_DATA_TX) { + // Reset state + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + } else { + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + print("SPI: TX unexpected state: "); puth(spi_state); print("\n"); + } +} + +void can_tx_comms_resume_spi(void) { + spi_can_tx_ready = true; +} diff --git a/board/drivers/spi.h b/board/drivers/spi.h index 283d845e0ac..6c66bc96799 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -1,15 +1,7 @@ #pragma once -#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 +#include +#include #define SPI_CHECKSUM_START 0xABU #define SPI_SYNC_BYTE 0x5AU @@ -27,232 +19,23 @@ enum { 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; -static const unsigned char version_text[] = "VERSION"; - -static uint16_t spi_version_packet(uint8_t *out) { - // this protocol version request is a stable portion of - // the panda's SPI protocol. its contents match that of the - // panda USB descriptors and are sufficent to list/enumerate - // a panda, determine panda type, and bootstub status. - - // the response is: - // VERSION + 2 byte data length + data + CRC8 - - // echo "VERSION" - (void)memcpy(out, version_text, 7); - - // write response - uint16_t data_len = 0; - uint16_t data_pos = 7U + 2U; - - // write serial - (void)memcpy(&out[data_pos], ((uint8_t *)UID_BASE), 12); - data_len += 12U; - - // HW type - out[data_pos + data_len] = hw_type; - data_len += 1U; - - // bootstub - out[data_pos + data_len] = USB_PID & 0xFFU; - data_len += 1U; - - // SPI protocol version - out[data_pos + data_len] = 0x2; - data_len += 1U; - - // data length - out[7] = data_len & 0xFFU; - out[8] = (data_len >> 8) & 0xFFU; +// got max rate from hitting a non-existent endpoint +// in a tight loop, plus some buffer +#define SPI_IRQ_RATE 16000U - // CRC8 - uint16_t resp_len = data_pos + data_len; - out[resp_len] = crc_checksum(out, resp_len, 0xD5U); - resp_len += 1U; +#define SPI_BUF_SIZE 4096U +extern uint8_t spi_buf_rx[SPI_BUF_SIZE]; +extern uint8_t spi_buf_tx[SPI_BUF_SIZE]; - return resp_len; -} +extern uint16_t spi_error_count; -void spi_init(void) { - // platform init - llspi_init(); +void spi_init(void); +void spi_rx_done(void); +void spi_tx_done(bool reset); - // Start the first packet! - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); -} - -static bool validate_checksum(const uint8_t *data, uint16_t len) { - // TODO: can speed this up by casting the bulk to uint32_t and xor-ing the bytes afterwards - uint8_t checksum = SPI_CHECKSUM_START; - for(uint16_t i = 0U; i < len; i++){ - checksum ^= data[i]; - } - return checksum == 0U; -} - -void spi_rx_done(void) { - uint16_t response_len = 0U; - uint8_t next_rx_state = SPI_STATE_HEADER_NACK; - bool checksum_valid = false; - static uint8_t spi_endpoint; - static uint16_t spi_data_len_miso; - - // parse header - spi_endpoint = spi_buf_rx[1]; - spi_data_len_mosi = (spi_buf_rx[3] << 8) | spi_buf_rx[2]; - spi_data_len_miso = (spi_buf_rx[5] << 8) | spi_buf_rx[4]; - - if (memcmp(spi_buf_rx, version_text, 7) == 0) { - response_len = spi_version_packet(spi_buf_tx); - next_rx_state = SPI_STATE_HEADER_NACK;; - } else if (spi_state == SPI_STATE_HEADER) { - checksum_valid = validate_checksum(spi_buf_rx, SPI_HEADER_SIZE); - if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && checksum_valid) { - // response: ACK and start receiving data portion - spi_buf_tx[0] = SPI_HACK; - next_rx_state = SPI_STATE_HEADER_ACK; - response_len = 1U; - } else { - // response: NACK and reset state machine - #ifdef DEBUG_SPI - print("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE); - #endif - spi_buf_tx[0] = SPI_NACK; - next_rx_state = SPI_STATE_HEADER_NACK; - response_len = 1U; - } - } else if (spi_state == SPI_STATE_DATA_RX) { - // We got everything! Based on the endpoint specified, call the appropriate handler - bool response_ack = false; - checksum_valid = validate_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U); - if (checksum_valid) { - if (spi_endpoint == 0U) { - if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { - ControlPacket_t ctrl = {0}; - (void)memcpy((uint8_t*)&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t)); - response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]); - response_ack = true; - } else { - print("SPI: insufficient data for control handler\n"); - } - } else if ((spi_endpoint == 1U) || (spi_endpoint == 0x81U)) { - if (spi_data_len_mosi == 0U) { - response_len = comms_can_read(&(spi_buf_tx[3]), spi_data_len_miso); - response_ack = true; - } else { - print("SPI: did not expect data for can_read\n"); - } - } else if (spi_endpoint == 2U) { - comms_endpoint2_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); - response_ack = true; - } else if (spi_endpoint == 3U) { - if (spi_data_len_mosi > 0U) { - if (spi_can_tx_ready) { - spi_can_tx_ready = false; - comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); - response_ack = true; - } else { - response_ack = false; - print("SPI: CAN NACK\n"); - } - } else { - print("SPI: did expect data for can_write\n"); - } - } else if (spi_endpoint == 0xABU) { - // test endpoint: mimics panda -> device transfer - response_len = spi_data_len_miso; - response_ack = true; - } else if (spi_endpoint == 0xACU) { - // test endpoint: mimics device -> panda transfer (with NACK) - response_ack = false; - } else { - print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); - } - } else { - // Checksum was incorrect - response_ack = false; - #ifdef DEBUG_SPI - print("- incorrect data checksum "); - puth4(spi_data_len_mosi); - print("\n"); - hexdump(spi_buf_rx, SPI_HEADER_SIZE); - hexdump(&(spi_buf_rx[SPI_HEADER_SIZE]), MIN(spi_data_len_mosi, 64)); - print("\n"); - #endif - } - - if (!response_ack) { - spi_buf_tx[0] = SPI_NACK; - next_rx_state = SPI_STATE_HEADER_NACK; - response_len = 1U; - } else { - // Setup response header - spi_buf_tx[0] = SPI_DACK; - spi_buf_tx[1] = response_len & 0xFFU; - spi_buf_tx[2] = (response_len >> 8) & 0xFFU; - - // Add checksum - uint8_t checksum = SPI_CHECKSUM_START; - for(uint16_t i = 0U; i < (response_len + 3U); i++) { - checksum ^= spi_buf_tx[i]; - } - spi_buf_tx[response_len + 3U] = checksum; - response_len += 4U; - - next_rx_state = SPI_STATE_DATA_TX; - } - } else { - print("SPI: RX unexpected state: "); puth(spi_state); print("\n"); - } - - // send out response - if (response_len == 0U) { - print("SPI: no response\n"); - spi_buf_tx[0] = SPI_NACK; - spi_state = SPI_STATE_HEADER_NACK; - response_len = 1U; - } - llspi_miso_dma(spi_buf_tx, response_len); - - spi_state = next_rx_state; - if (!checksum_valid) { - spi_error_count += 1U; - } -} - -void spi_tx_done(bool reset) { - if ((spi_state == SPI_STATE_HEADER_NACK) || reset) { - // Reset state - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else if (spi_state == SPI_STATE_HEADER_ACK) { - // ACK was sent, queue up the RX buf for the data + checksum - spi_state = SPI_STATE_DATA_RX; - llspi_mosi_dma(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi + 1U); - } else if (spi_state == SPI_STATE_DATA_TX) { - // Reset state - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else { - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - print("SPI: TX unexpected state: "); puth(spi_state); print("\n"); - } -} - -void can_tx_comms_resume_spi(void) { - spi_can_tx_ready = true; -} +// 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); diff --git a/board/drivers/timers.c b/board/drivers/timers.c new file mode 100644 index 00000000000..3cf9186f92b --- /dev/null +++ b/board/drivers/timers.c @@ -0,0 +1,34 @@ +#include "board/config.h" +#include "board/drivers/timers.h" + +static void timer_init(TIM_TypeDef *TIM, int psc) { + register_set(&(TIM->PSC), (psc-1), 0xFFFFU); + register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); + register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); + TIM->SR = 0; +} + +void microsecond_timer_init(void) { + MICROSECOND_TIMER->PSC = (APB1_TIMER_FREQ - 1U); + MICROSECOND_TIMER->CR1 = TIM_CR1_CEN; + MICROSECOND_TIMER->EGR = TIM_EGR_UG; +} + +uint32_t microsecond_timer_get(void) { + return MICROSECOND_TIMER->CNT; +} + +void interrupt_timer_init(void) { + enable_interrupt_timer(); + REGISTER_INTERRUPT(INTERRUPT_TIMER_IRQ, interrupt_timer_handler, 2U, FAULT_INTERRUPT_RATE_INTERRUPTS) + register_set(&(INTERRUPT_TIMER->PSC), ((uint16_t)(15.25*APB1_TIMER_FREQ)-1U), 0xFFFFU); + register_set(&(INTERRUPT_TIMER->DIER), TIM_DIER_UIE, 0x5F5FU); + register_set(&(INTERRUPT_TIMER->CR1), TIM_CR1_CEN, 0x3FU); + INTERRUPT_TIMER->SR = 0; + NVIC_EnableIRQ(INTERRUPT_TIMER_IRQ); +} + +void tick_timer_init(void) { + timer_init(TICK_TIMER, (uint16_t)((15.25*APB2_TIMER_FREQ)/8U)); + NVIC_EnableIRQ(TICK_TIMER_IRQ); +} diff --git a/board/drivers/timers.h b/board/drivers/timers.h index 4c046a2b49b..cd3cb2e9e12 100644 --- a/board/drivers/timers.h +++ b/board/drivers/timers.h @@ -1,31 +1,9 @@ -static void timer_init(TIM_TypeDef *TIM, int psc) { - register_set(&(TIM->PSC), (psc-1), 0xFFFFU); - register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); - register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); - TIM->SR = 0; -} +#pragma once -void microsecond_timer_init(void) { - MICROSECOND_TIMER->PSC = (APB1_TIMER_FREQ - 1U); - MICROSECOND_TIMER->CR1 = TIM_CR1_CEN; - MICROSECOND_TIMER->EGR = TIM_EGR_UG; -} +#include -uint32_t microsecond_timer_get(void) { - return MICROSECOND_TIMER->CNT; -} - -void interrupt_timer_init(void) { - enable_interrupt_timer(); - REGISTER_INTERRUPT(INTERRUPT_TIMER_IRQ, interrupt_timer_handler, 2U, FAULT_INTERRUPT_RATE_INTERRUPTS) - register_set(&(INTERRUPT_TIMER->PSC), ((uint16_t)(15.25*APB1_TIMER_FREQ)-1U), 0xFFFFU); - register_set(&(INTERRUPT_TIMER->DIER), TIM_DIER_UIE, 0x5F5FU); - register_set(&(INTERRUPT_TIMER->CR1), TIM_CR1_CEN, 0x3FU); - INTERRUPT_TIMER->SR = 0; - NVIC_EnableIRQ(INTERRUPT_TIMER_IRQ); -} - -void tick_timer_init(void) { - timer_init(TICK_TIMER, (uint16_t)((15.25*APB2_TIMER_FREQ)/8U)); - NVIC_EnableIRQ(TICK_TIMER_IRQ); -} +void microsecond_timer_init(void); +uint32_t microsecond_timer_get(void); +void interrupt_timer_init(void); +void tick_timer_init(void); +void enable_interrupt_timer(void); diff --git a/board/drivers/uart.c b/board/drivers/uart.c new file mode 100644 index 00000000000..33bff204d68 --- /dev/null +++ b/board/drivers/uart.c @@ -0,0 +1,154 @@ +#include "board/config.h" +#include "board/drivers/uart.h" + +// ***************************** Definitions ***************************** +#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, overwrite_mode) \ + static uint8_t elems_rx_##x[size_rx]; \ + static uint8_t elems_tx_##x[size_tx]; \ + extern uart_ring uart_ring_##x; \ + uart_ring uart_ring_##x = { \ + .w_ptr_tx = 0, \ + .r_ptr_tx = 0, \ + .elems_tx = ((uint8_t *)&(elems_tx_##x)), \ + .tx_fifo_size = (size_tx), \ + .w_ptr_rx = 0, \ + .r_ptr_rx = 0, \ + .elems_rx = ((uint8_t *)&(elems_rx_##x)), \ + .rx_fifo_size = (size_rx), \ + .uart = (uart_ptr), \ + .callback = (callback_ptr), \ + .overwrite = (overwrite_mode) \ + }; + +// ******************************** UART buffers ******************************** +// debug = USART2 +UART_BUFFER(debug, FIFO_SIZE_INT, FIFO_SIZE_INT, USART2, debug_ring_callback, true) + +// SOM debug = UART7 +UART_BUFFER(som_debug, FIFO_SIZE_INT, FIFO_SIZE_INT, UART7, NULL, true) + +uart_ring *get_ring_by_number(int a) { + uart_ring *ring = NULL; + switch(a) { + case 0: + ring = &uart_ring_debug; + break; + case 4: + ring = &uart_ring_som_debug; + break; + default: + ring = NULL; + break; + } + return ring; +} + +// ************************* Low-level buffer functions ************************* +bool get_char(uart_ring *q, char *elem) { + bool ret = false; + + ENTER_CRITICAL(); + if (q->w_ptr_rx != q->r_ptr_rx) { + if (elem != NULL) { + *elem = q->elems_rx[q->r_ptr_rx]; + } + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + ret = true; + } + EXIT_CRITICAL(); + + return ret; +} + +bool injectc(uart_ring *q, char elem) { + int ret = false; + uint16_t next_w_ptr; + + ENTER_CRITICAL(); + next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; + + if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { + // overwrite mode: drop oldest byte + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + } + + if (next_w_ptr != q->r_ptr_rx) { + q->elems_rx[q->w_ptr_rx] = elem; + q->w_ptr_rx = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + + return ret; +} + +bool put_char(uart_ring *q, char elem) { + bool ret = false; + uint16_t next_w_ptr; + + ENTER_CRITICAL(); + next_w_ptr = (q->w_ptr_tx + 1U) % q->tx_fifo_size; + + if ((next_w_ptr == q->r_ptr_tx) && q->overwrite) { + // overwrite mode: drop oldest byte + q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; + } + + if (next_w_ptr != q->r_ptr_tx) { + q->elems_tx[q->w_ptr_tx] = elem; + q->w_ptr_tx = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + + uart_tx_ring(q); + + return ret; +} + +// ************************ High-level debug functions ********************** +void putch(const char a) { + // misra-c2012-17.7: serial debug function, ok to ignore output + (void)injectc(&uart_ring_debug, a); +} + +void print(const char *a) { + for (const char *in = a; *in; in++) { + if (*in == '\n') { + putch('\r'); + } + putch(*in); + } +} + +void puthx(uint32_t i, uint8_t len) { + const char c[] = "0123456789abcdef"; + for (int pos = ((int)len * 4) - 4; pos > -4; pos -= 4) { + putch(c[(i >> (unsigned int)(pos)) & 0xFU]); + } +} + +void puth(unsigned int i) { + puthx(i, 8U); +} + +#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG) +void puth4(unsigned int i) { + puthx(i, 4U); +} +#endif + +#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG_USB) || defined(DEBUG_COMMS) +void hexdump(const void *a, int l) { + if (a != NULL) { + for (int i=0; i < l; i++) { + if ((i != 0) && ((i & 0xf) == 0)) { + print("\n"); + } + puthx(((const unsigned char*)a)[i], 2U); + print(" "); + } + } + print("\n"); +} +#endif diff --git a/board/drivers/uart.h b/board/drivers/uart.h index 250c2ea0010..3bef83743f9 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -1,149 +1,44 @@ -#include "board/drivers/drivers.h" - -// ***************************** Definitions ***************************** - -#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, overwrite_mode) \ - static uint8_t elems_rx_##x[size_rx]; \ - static uint8_t elems_tx_##x[size_tx]; \ - extern uart_ring uart_ring_##x; \ - uart_ring uart_ring_##x = { \ - .w_ptr_tx = 0, \ - .r_ptr_tx = 0, \ - .elems_tx = ((uint8_t *)&(elems_tx_##x)), \ - .tx_fifo_size = (size_tx), \ - .w_ptr_rx = 0, \ - .r_ptr_rx = 0, \ - .elems_rx = ((uint8_t *)&(elems_rx_##x)), \ - .rx_fifo_size = (size_rx), \ - .uart = (uart_ptr), \ - .callback = (callback_ptr), \ - .overwrite = (overwrite_mode) \ - }; - -// ******************************** UART buffers ******************************** - -// debug = USART2 -UART_BUFFER(debug, FIFO_SIZE_INT, FIFO_SIZE_INT, USART2, debug_ring_callback, true) - -// SOM debug = UART7 -UART_BUFFER(som_debug, FIFO_SIZE_INT, FIFO_SIZE_INT, UART7, NULL, true) - -uart_ring *get_ring_by_number(int a) { - uart_ring *ring = NULL; - switch(a) { - case 0: - ring = &uart_ring_debug; - break; - case 4: - ring = &uart_ring_som_debug; - break; - default: - ring = NULL; - break; - } - return ring; -} - -// ************************* Low-level buffer functions ************************* -bool get_char(uart_ring *q, char *elem) { - bool ret = false; - - ENTER_CRITICAL(); - if (q->w_ptr_rx != q->r_ptr_rx) { - if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx]; - q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; - ret = true; - } - EXIT_CRITICAL(); - - return ret; -} - -bool injectc(uart_ring *q, char elem) { - int ret = false; - uint16_t next_w_ptr; - - ENTER_CRITICAL(); - next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; - - if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { - // overwrite mode: drop oldest byte - q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; - } - - if (next_w_ptr != q->r_ptr_rx) { - q->elems_rx[q->w_ptr_rx] = elem; - q->w_ptr_rx = next_w_ptr; - ret = true; - } - EXIT_CRITICAL(); - - return ret; -} - -bool put_char(uart_ring *q, char elem) { - bool ret = false; - uint16_t next_w_ptr; - - ENTER_CRITICAL(); - next_w_ptr = (q->w_ptr_tx + 1U) % q->tx_fifo_size; - - if ((next_w_ptr == q->r_ptr_tx) && q->overwrite) { - // overwrite mode: drop oldest byte - q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; - } - - if (next_w_ptr != q->r_ptr_tx) { - q->elems_tx[q->w_ptr_tx] = elem; - q->w_ptr_tx = next_w_ptr; - ret = true; - } - EXIT_CRITICAL(); - - uart_tx_ring(q); - - return ret; -} - -// ************************ High-level debug functions ********************** -void putch(const char a) { - // misra-c2012-17.7: serial debug function, ok to ignore output - (void)injectc(&uart_ring_debug, a); -} - -void print(const char *a) { - for (const char *in = a; *in; in++) { - if (*in == '\n') putch('\r'); - putch(*in); - } -} - -void puthx(uint32_t i, uint8_t len) { - const char c[] = "0123456789abcdef"; - for (int pos = ((int)len * 4) - 4; pos > -4; pos -= 4) { - putch(c[(i >> (unsigned int)(pos)) & 0xFU]); - } -} - -void puth(unsigned int i) { - puthx(i, 8U); -} - +#pragma once + +#include +#include + +#ifdef STM32H7 + +#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; + +extern uart_ring uart_ring_debug; +extern uart_ring uart_ring_som_debug; + +void debug_ring_callback(uart_ring *ring); +void uart_tx_ring(uart_ring *q); +uart_ring *get_ring_by_number(int a); +bool get_char(uart_ring *q, char *elem); +bool injectc(uart_ring *q, char elem); +bool put_char(uart_ring *q, char elem); +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) { - puthx(i, 4U); -} +void puth4(unsigned int i); #endif - #if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG_USB) || defined(DEBUG_COMMS) -static void hexdump(const void *a, int l) { - if (a != NULL) { - for (int i=0; i < l; i++) { - if ((i != 0) && ((i & 0xf) == 0)) print("\n"); - puthx(((const unsigned char*)a)[i], 2U); - print(" "); - } - } - print("\n"); -} +void hexdump(const void *a, int l); #endif + +#endif // STM32H7 diff --git a/board/drivers/usb.c b/board/drivers/usb.c new file mode 100644 index 00000000000..0b84fa93ded --- /dev/null +++ b/board/drivers/usb.c @@ -0,0 +1,800 @@ +#include "board/config.h" +#include "board/drivers/usb.h" +#include "board/can_comms.h" + +static uint8_t response[USBPACKET_MAX_SIZE]; + +// current packet +static USB_Setup_TypeDef setup; +static uint8_t* ep0_txdata = NULL; +static uint16_t ep0_txlen = 0; +static bool outep3_processing = false; + +// Store the current interface alt setting. +static int current_int0_alt_setting = 0; + +// packet read and write +static void *USB_ReadPacket(void *dest, uint16_t len) { + uint32_t *dest_copy = (uint32_t *)dest; + uint32_t count32b = ((uint32_t)len + 3U) / 4U; + + for (uint32_t i = 0; i < count32b; i++) { + *dest_copy = USBx_DFIFO(0U); + dest_copy++; + } + return ((void *)dest_copy); +} + +static void USB_WritePacket(const void *src, uint16_t len, uint32_t ep) { + #ifdef DEBUG_USB + print("writing "); + hexdump(src, len); + #endif + + uint32_t numpacket = ((uint32_t)len + (USBPACKET_MAX_SIZE - 1U)) / USBPACKET_MAX_SIZE; + uint32_t count32b = 0; + count32b = ((uint32_t)len + 3U) / 4U; + + // TODO: revisit this + USBx_INEP(ep)->DIEPTSIZ = ((numpacket << 19) & USB_OTG_DIEPTSIZ_PKTCNT) | + (len & USB_OTG_DIEPTSIZ_XFRSIZ); + USBx_INEP(ep)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA); + + // load the FIFO + if (src != NULL) { + const uint32_t *src_copy = (const uint32_t *)src; + // load the FIFO + for (uint32_t i = 0; i < count32b; i++) { + USBx_DFIFO(ep) = *src_copy; + src_copy++; + } + } +} + +// IN EP 0 TX FIFO has a max size of 127 bytes (much smaller than the rest) +// so use TX FIFO empty interrupt to send larger amounts of data +static void USB_WritePacket_EP0(uint8_t *src, uint16_t len) { + #ifdef DEBUG_USB + print("writing "); + hexdump(src, len); + #endif + + uint16_t wplen = MIN(len, 0x40); + USB_WritePacket(src, wplen, 0); + + if (wplen < len) { + ep0_txdata = &src[wplen]; + ep0_txlen = len - wplen; + USBx_DEVICE->DIEPEMPMSK |= 1; + } else { + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } +} + +static void usb_reset(void) { + // unmask endpoint interrupts, so many sets + USBx_DEVICE->DAINT = 0xFFFFFFFFU; + USBx_DEVICE->DAINTMSK = 0xFFFFFFFFU; + + //USBx_DEVICE->DOEPMSK = (USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_EPDM); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM | USB_OTG_DIEPMSK_ITTXFEMSK); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM); + // all interrupts for debugging + USBx_DEVICE->DIEPMSK = 0xFFFFFFFFU; + USBx_DEVICE->DOEPMSK = 0xFFFFFFFFU; + + // clear interrupts + USBx_INEP(0U)->DIEPINT = 0xFF; + USBx_OUTEP(0U)->DOEPINT = 0xFF; + + // unset the address + USBx_DEVICE->DCFG &= ~USB_OTG_DCFG_DAD; + + // set up USB FIFOs + // RX start address is fixed to 0 + USBx->GRXFSIZ = 0x40; + + // 0x100 to offset past GRXFSIZ + USBx->DIEPTXF0_HNPTXFSIZ = (0x40UL << 16) | 0x40U; + + // EP1, massive + USBx->DIEPTXF[0] = (0x40UL << 16) | 0x80U; + + // flush TX fifo + USBx->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | USB_OTG_GRSTCTL_TXFNUM_4; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH) {} + // flush RX FIFO + USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH) {} + + // no global NAK + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGINAK; + + // ready to receive setup packets + USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (3U << 3); +} + +static char to_hex_char(uint8_t a) { + char ret; + if (a < 10U) { + ret = '0' + a; + } else { + ret = 'a' + (a - 10U); + } + return ret; +} + +static void usb_setup(void) { + static uint8_t device_desc[] = { + DSCR_DEVICE_LEN, USB_DESC_TYPE_DEVICE, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // Class, Subclass, Protocol, Max Packet Size + TOUSBORDER(USB_VID), // idVendor + TOUSBORDER(USB_PID), // idProduct + 0x00, 0x00, // bcdDevice + 0x01, 0x02, // Manufacturer, Product + 0x03, 0x01 // Serial Number, Num Configurations + }; + + static uint8_t device_qualifier[] = { + 0x0a, USB_DESC_TYPE_DEVICE_QUALIFIER, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0 + 0x01, 0x00 // bNumConfigurations, bReserved + }; + + static uint8_t configuration_desc[] = { + DSCR_CONFIG_LEN, USB_DESC_TYPE_CONFIGURATION, // Length, Type, + TOUSBORDER(0x0045U), // Total Len (uint16) + 0x01, 0x01, STRING_OFFSET_ICONFIGURATION, // Num Interface, Config Value, Configuration + 0xc0, 0x32, // Attributes, Max Power + // interface 0 ALT 0 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x00, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval (NA) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // interface 0 ALT 1 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x01, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_INT, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x05, // Polling Interval (5 frames) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + }; + + // STRING_DESCRIPTOR_HEADER is for uint16 string descriptors + // it takes in a string length, which is bytes/2 because unicode + static uint16_t string_language_desc[] = { + STRING_DESCRIPTOR_HEADER(1), + 0x0409 // american english + }; + + // these strings are all uint16's so that we don't need to spam ,0 after every character + static uint16_t string_manufacturer_desc[] = { + STRING_DESCRIPTOR_HEADER(8), + 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' + }; + + static uint16_t string_product_desc[] = { + STRING_DESCRIPTOR_HEADER(5), + 'p', 'a', 'n', 'd', 'a' + }; + + // a string containing the default configuration index + static uint16_t string_configuration_desc[] = { + STRING_DESCRIPTOR_HEADER(2), + '0', '1' // "01" + }; + + // WCID (auto install WinUSB driver) + // https://github.com/pbatard/libwdi/wiki/WCID-Devices + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation#automatic-installation-of--winusb-without-an-inf-file + // WinUSB 1.0 descriptors, this is mostly used by Windows XP + static uint8_t string_238_desc[] = { + 0x12, USB_DESC_TYPE_STRING, // bLength, bDescriptorType + 'M',0, 'S',0, 'F',0, 'T',0, '1',0, '0',0, '0',0, // qwSignature (MSFT100) + MS_VENDOR_CODE, 0x00 // bMS_VendorCode, bPad + }; + + static uint8_t winusb_ext_compatid_os_desc[] = { + 0x28, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x04, 0x00, // wIndex + 0x01, // bCount + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved + 0x00, // bFirstInterfaceNumber + 0x00, // Reserved + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subcompatible ID (none) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved + }; + + static uint8_t winusb_ext_prop_os_desc[] = { + 0x8e, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x05, 0x00, // wIndex + 0x01, 0x00, // wCount + // first property + 0x84, 0x00, 0x00, 0x00, // dwSize + 0x01, 0x00, 0x00, 0x00, // dwPropertyDataType + 0x28, 0x00, // wPropertyNameLength + 'D',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 'I',0, 'n',0, 't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0, 'G',0, 'U',0, 'I',0, 'D',0, 0, 0, // bPropertyName (DeviceInterfaceGUID) + 0x4e, 0x00, 0x00, 0x00, // dwPropertyDataLength + '{',0, 'c',0, 'c',0, 'e',0, '5',0, '2',0, '9',0, '1',0, 'c',0, '-',0, 'a',0, '6',0, '9',0, 'f',0, '-',0, '4',0 ,'9',0 ,'9',0 ,'5',0 ,'-',0, 'a',0, '4',0, 'c',0, '2',0, '-',0, '2',0, 'a',0, 'e',0, '5',0, '7',0, 'a',0, '5',0, '1',0, 'a',0, 'd',0, 'e',0, '9',0, '}',0, 0, 0, // bPropertyData ({CCE5291C-A69F-4995-A4C2-2AE57A51ADE9}) + }; + + /* + Binary Object Store descriptor used to expose WebUSB (and more WinUSB) metadata + comments are from the wicg spec + References used: + https://wicg.github.io/webusb/#webusb-platform-capability-descriptor + https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c + https://os.mbed.com/users/larsgk/code/USBDevice_WebUSB/file/1d8a6665d607/WebUSBDevice/ + */ + static uint8_t binary_object_store_desc[] = { + // BOS header + BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header + BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType + 0x39, 0x00, // wTotalLength (LSB, MSB) + 0x02, // bNumDeviceCaps (WebUSB + WinUSB) + + // ------------------------------------------------- + // WebUSB descriptor + // header + 0x18, // bLength, Size of this descriptor. Must be set to 24. + 0x10, // bDescriptorType, DEVICE CAPABILITY descriptor + 0x05, // bDevCapabilityType, PLATFORM capability + 0x00, // bReserved, This field is reserved and shall be set to zero. + + // PlatformCapabilityUUID, Must be set to {3408b638-09a9-47a0-8bfd-a0768815b665}. + 0x38, 0xB6, 0x08, 0x34, + 0xA9, 0x09, 0xA0, 0x47, + 0x8B, 0xFD, 0xA0, 0x76, + 0x88, 0x15, 0xB6, 0x65, + + // + 0x00, 0x01, // bcdVersion, Protocol version supported. Must be set to 0x0100. + WEBUSB_VENDOR_CODE, // bVendorCode, bRequest value used for issuing WebUSB requests. + // there used to be a concept of "allowed origins", but it was removed from the spec + // it was intended to be a security feature, but then the entire security model relies on domain ownership + // https://github.com/WICG/webusb/issues/49 + // other implementations use various other indexed to leverate this no-longer-valid feature. we wont. + // the spec says we *must* reply to index 0x03 with the url, so we'll hint that that's the right index + 0x03, // iLandingPage, URL descriptor index of the device’s landing page. + + // ------------------------------------------------- + // WinUSB descriptor + // header + 0x1C, // Descriptor size (28 bytes) + 0x10, // Descriptor type (Device Capability) + 0x05, // Capability type (Platform) + 0x00, // Reserved + + // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) + // Indicates the device supports the Microsoft OS 2.0 descriptor + 0xDF, 0x60, 0xDD, 0xD8, + 0x89, 0x45, 0xC7, 0x4C, + 0x9C, 0xD2, 0x65, 0x9D, + 0x9E, 0x64, 0x8A, 0x9F, + + 0x00, 0x00, 0x03, 0x06, // Windows version, currently set to 8.1 (0x06030000) + + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // MS OS 2.0 descriptor size (word) + MS_VENDOR_CODE, 0x00 // vendor code, no alternate enumeration + }; + + // WinUSB 2.0 descriptor. This is what modern systems use + // https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c + // http://janaxelson.com/files/ms_os_20_descriptors.c + // https://books.google.com/books?id=pkefBgAAQBAJ&pg=PA353&lpg=PA353 + static uint8_t winusb_20_desc[WINUSB_PLATFORM_DESCRIPTOR_LENGTH] = { + // Microsoft OS 2.0 descriptor set header (table 10) + 0x0A, 0x00, // Descriptor size (10 bytes) + 0x00, 0x00, // MS OS 2.0 descriptor set header + + 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // Total size of MS OS 2.0 descriptor set + + // Microsoft OS 2.0 compatible ID descriptor + 0x14, 0x00, // Descriptor size (20 bytes) + 0x03, 0x00, // MS OS 2.0 compatible ID descriptor + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-compatible ID + + // Registry property descriptor + 0x80, 0x00, // Descriptor size (130 bytes) + 0x04, 0x00, // Registry Property descriptor + 0x01, 0x00, // Strings are null-terminated Unicode + 0x28, 0x00, // Size of Property Name (40 bytes) "DeviceInterfaceGUID" + + // bPropertyName (DeviceInterfaceGUID) + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, + 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, + 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, + + 0x4E, 0x00, // Size of Property Data (78 bytes) + + // Vendor-defined property data: {CCE5291C-A69F-4995-A4C2-2AE57A51ADE9} + '{', 0x00, 'c', 0x00, 'c', 0x00, 'e', 0x00, '5', 0x00, '2', 0x00, '9', 0x00, '1', 0x00, // 16 + 'c', 0x00, '-', 0x00, 'a', 0x00, '6', 0x00, '9', 0x00, 'f', 0x00, '-', 0x00, '4', 0x00, // 32 + '9', 0x00, '9', 0x00, '5', 0x00, '-', 0x00, 'a', 0x00, '4', 0x00, 'c', 0x00, '2', 0x00, // 48 + '-', 0x00, '2', 0x00, 'a', 0x00, 'e', 0x00, '5', 0x00, '7', 0x00, 'a', 0x00, '5', 0x00, // 64 + '1', 0x00, 'a', 0x00, 'd', 0x00, 'e', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00 // 78 bytes + }; + + int resp_len; + ControlPacket_t control_req; + + // setup packet is ready + switch (setup.b.bRequest) { + case USB_REQ_SET_CONFIGURATION: + // enable other endpoints, has to be here? + USBx_INEP(1U)->DIEPCTL = (0x40U & USB_OTG_DIEPCTL_MPSIZ) | (2UL << 18) | (1UL << 22) | + USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DIEPCTL_USBAEP; + USBx_INEP(1U)->DIEPINT = 0xFF; + + USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; + USBx_OUTEP(2U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(2U)->DOEPINT = 0xFF; + + USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; + USBx_OUTEP(3U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(3U)->DOEPINT = 0xFF; + + // mark ready to receive + USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_ADDRESS: + // set now? + USBx_DEVICE->DCFG |= ((setup.b.wValue.w & 0x7fU) << 4); + + #ifdef DEBUG_USB + print(" set address\n"); + #endif + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + break; + case USB_REQ_GET_DESCRIPTOR: + switch (setup.b.wValue.bw.lsb) { + case USB_DESC_TYPE_DEVICE: + //print(" writing device descriptor\n"); + // set bcdDevice to hardware type + device_desc[13] = hw_type; + // setup transfer + USB_WritePacket(device_desc, MIN(sizeof(device_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + //print("D"); + break; + case USB_DESC_TYPE_CONFIGURATION: + USB_WritePacket(configuration_desc, MIN(sizeof(configuration_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_DEVICE_QUALIFIER: + USB_WritePacket(device_qualifier, MIN(sizeof(device_qualifier), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_STRING: + switch (setup.b.wValue.bw.msb) { + case STRING_OFFSET_LANGID: + USB_WritePacket((uint8_t*)string_language_desc, MIN(sizeof(string_language_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IMANUFACTURER: + USB_WritePacket((uint8_t*)string_manufacturer_desc, MIN(sizeof(string_manufacturer_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IPRODUCT: + USB_WritePacket((uint8_t*)string_product_desc, MIN(sizeof(string_product_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_ISERIAL: + response[0] = 0x02 + (12 * 4); + response[1] = 0x03; + + // 96 bits = 12 bytes + for (int i = 0; i < 12; i++){ + uint8_t cc = ((uint8_t *)UID_BASE)[i]; + response[2 + (i * 4)] = to_hex_char((cc >> 4) & 0xFU); + response[2 + (i * 4) + 1] = '\0'; + response[2 + (i * 4) + 2] = to_hex_char((cc >> 0) & 0xFU); + response[2 + (i * 4) + 3] = '\0'; + } + + USB_WritePacket(response, MIN(response[0], setup.b.wLength.w), 0); + break; + case STRING_OFFSET_ICONFIGURATION: + USB_WritePacket((uint8_t*)string_configuration_desc, MIN(sizeof(string_configuration_desc), setup.b.wLength.w), 0); + break; + case 238: + USB_WritePacket((uint8_t*)string_238_desc, MIN(sizeof(string_238_desc), setup.b.wLength.w), 0); + break; + default: + // nothing + USB_WritePacket(0, 0, 0); + break; + } + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_BINARY_OBJECT_STORE: + USB_WritePacket(binary_object_store_desc, MIN(sizeof(binary_object_store_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + default: + // nothing here? + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + } + break; + case USB_REQ_GET_STATUS: + // empty response? + response[0] = 0; + response[1] = 0; + USB_WritePacket((void*)&response, 2, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_INTERFACE: + // Store the alt setting number for IN EP behavior. + current_int0_alt_setting = setup.b.wValue.w; + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case WEBUSB_VENDOR_CODE: + // probably asking for allowed origins, which was removed from the spec + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case MS_VENDOR_CODE: + switch (setup.b.wIndex.w) { + // winusb 2.0 descriptor from BOS + case WINUSB_REQ_GET_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_20_desc, MIN(sizeof(winusb_20_desc), setup.b.wLength.w)); + break; + // Extended Compat ID OS Descriptor + case WINUSB_REQ_GET_COMPATID_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_ext_compatid_os_desc, MIN(sizeof(winusb_ext_compatid_os_desc), setup.b.wLength.w)); + break; + // Extended Properties OS Descriptor + case WINUSB_REQ_GET_EXT_PROPS_OS: + USB_WritePacket_EP0((uint8_t*)winusb_ext_prop_os_desc, MIN(sizeof(winusb_ext_prop_os_desc), setup.b.wLength.w)); + break; + default: + USB_WritePacket_EP0(0, 0); + break; + } + break; + default: + control_req.request = setup.b.bRequest; + control_req.param1 = setup.b.wValue.w; + control_req.param2 = setup.b.wIndex.w; + control_req.length = setup.b.wLength.w; + + resp_len = comms_control_handler(&control_req, response); + USB_WritePacket(response, MIN(resp_len, setup.b.wLength.w), 0); + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + } +} + +// ***************************** USB port ***************************** +void usb_irqhandler(void) { + //USBx->GINTMSK = 0; + static uint8_t usbdata[0x100] __attribute__((aligned(4))); + unsigned int gintsts = USBx->GINTSTS; + unsigned int gotgint = USBx->GOTGINT; + unsigned int daint = USBx_DEVICE->DAINT; + + // gintsts SUSPEND? 04008428 + #ifdef DEBUG_USB + puth(gintsts); + print(" "); + /*puth(USBx->GCCFG); + print(" ");*/ + puth(gotgint); + print(" ep "); + puth(daint); + print(" USB interrupt!\n"); + #endif + + if ((gintsts & USB_OTG_GINTSTS_CIDSCHG) != 0U) { + print("connector ID status change\n"); + } + + if ((gintsts & USB_OTG_GINTSTS_USBRST) != 0U) { + #ifdef DEBUG_USB + print("USB reset\n"); + #endif + usb_reset(); + } + + if ((gintsts & USB_OTG_GINTSTS_ENUMDNE) != 0U) { + #ifdef DEBUG_USB + print("enumeration done\n"); + #endif + } + + if ((gintsts & USB_OTG_GINTSTS_OTGINT) != 0U) { + #ifdef DEBUG_USB + print("OTG int:"); + puth(USBx->GOTGINT); + print("\n"); + #endif + // Full speed, ENUMSPD + //puth(USBx_DEVICE->DSTS); + } + + // RX FIFO first + if ((gintsts & USB_OTG_GINTSTS_RXFLVL) != 0U) { + // 1. Read the Receive status pop register + volatile unsigned int rxst = USBx->GRXSTSP; + int status = (rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17; + + #ifdef DEBUG_USB + print(" RX FIFO:"); + puth(rxst); + print(" status: "); + puth(status); + print(" len: "); + puth((rxst & USB_OTG_GRXSTSP_BCNT) >> 4); + print("\n"); + #endif + + if (status == STS_DATA_UPDT) { + int endpoint = (rxst & USB_OTG_GRXSTSP_EPNUM); + int len = (rxst & USB_OTG_GRXSTSP_BCNT) >> 4; + (void)USB_ReadPacket(&usbdata, len); + #ifdef DEBUG_USB + print(" data "); + puth(len); + print("\n"); + hexdump(&usbdata, len); + #endif + + if (endpoint == 2) { + comms_endpoint2_write((uint8_t *) usbdata, len); + // getting ADTOCHG + //USBx->GOTGINT = USBx->GOTGINT; + } + + if (endpoint == 3) { + outep3_processing = true; + comms_can_write(usbdata, len); + } + } else if (status == STS_SETUP_UPDT) { + (void)USB_ReadPacket(&setup, 8); + #ifdef DEBUG_USB + print(" setup "); + hexdump(&setup, 8); + print("\n"); + #endif + } else { + // status is neither STS_DATA_UPDT or STS_SETUP_UPDT, skip + } + } + + /*if (gintsts & USB_OTG_GINTSTS_HPRTINT) { + print("HPRT:"); + puth(USBx_HOST_PORT->HPRT); + print("\n"); + if (USBx_HOST_PORT->HPRT & USB_OTG_HPRT_PCDET) { + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PRST; + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PCDET; + } + }*/ + + if ((gintsts & USB_OTG_GINTSTS_BOUTNAKEFF) || (gintsts & USB_OTG_GINTSTS_GINAKEFF)) { + // no global NAK, why is this getting set? + #ifdef DEBUG_USB + print("GLOBAL NAK\n"); + #endif + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK | USB_OTG_DCTL_CGINAK; + } + + if ((gintsts & USB_OTG_GINTSTS_SRQINT) != 0U) { + // we want to do "A-device host negotiation protocol" since we are the A-device + /*print("start request\n"); + puth(USBx->GOTGCTL); + print("\n");*/ + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + //USBx_HOST_PORT->HPRT = USB_OTG_HPRT_PPWR | USB_OTG_HPRT_PENA; + //USBx->GOTGCTL |= USB_OTG_GOTGCTL_SRQ; + } + + // out endpoint hit + if ((gintsts & USB_OTG_GINTSTS_OEPINT) != 0U) { + #ifdef DEBUG_USB + print(" 0:"); + puth(USBx_OUTEP(0U)->DOEPINT); + print(" 2:"); + puth(USBx_OUTEP(2U)->DOEPINT); + print(" 3:"); + puth(USBx_OUTEP(3U)->DOEPINT); + print(" "); + puth(USBx_OUTEP(3U)->DOEPCTL); + print(" 4:"); + puth(USBx_OUTEP(4)->DOEPINT); + print(" OUT ENDPOINT\n"); + #endif + + if ((USBx_OUTEP(2U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { + #ifdef DEBUG_USB + print(" OUT2 PACKET XFRC\n"); + #endif + USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; + USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } + + if ((USBx_OUTEP(3U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { + #ifdef DEBUG_USB + print(" OUT3 PACKET XFRC\n"); + #endif + // NAK cleared by process_can (if tx buffers have room) + outep3_processing = false; + refresh_can_tx_slots_available(); + } else if ((USBx_OUTEP(3U)->DOEPINT & 0x2000U) != 0U) { + #ifdef DEBUG_USB + print(" OUT3 PACKET WTF\n"); + #endif + // if NAK was set trigger this, unknown interrupt + // TODO: why was this here? fires when TX buffers when we can't clear NAK + // USBx_OUTEP(3U)->DOEPTSIZ = (1U << 19) | 0x40U; + // USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } else if ((USBx_OUTEP(3U)->DOEPINT) != 0U) { + #ifdef DEBUG_USB + print("OUTEP3 error "); + puth(USBx_OUTEP(3U)->DOEPINT); + print("\n"); + #endif + } else { + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + //USBx_HOST_PORT->HPRT = USB_OTG_HPRT_PPWR | USB_OTG_HPRT_PENA; + //USBx->GOTGCTL |= USB_OTG_GOTGCTL_SRQ; + } + + if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DIEPINT_XFRC) != 0U) { + // ready for next packet + USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (1U << 3); + } + + // respond to setup packets + if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DOEPINT_STUP) != 0U) { + usb_setup(); + // USBx_OUTEP(3U)->DOEPINT is 0, ok to skip + } + + USBx_OUTEP(0U)->DOEPINT = USBx_OUTEP(0U)->DOEPINT; + USBx_OUTEP(2U)->DOEPINT = USBx_OUTEP(2U)->DOEPINT; + USBx_OUTEP(3U)->DOEPINT = USBx_OUTEP(3U)->DOEPINT; + } + + // interrupt endpoint hit (Page 1221) + if ((gintsts & USB_OTG_GINTSTS_IEPINT) != 0U) { + #ifdef DEBUG_USB + print(" "); + puth(USBx_INEP(0U)->DIEPINT); + print(" "); + puth(USBx_INEP(1U)->DIEPINT); + print(" IN ENDPOINT\n"); + #endif + + // Should likely check the EP of the IN request even if there is + // only one IN endpoint. + // No need to set NAK in OTG_DIEPCTL0 when nothing to send, + // Appears USB core automatically sets NAK. WritePacket clears it. + // Handle the two interface alternate settings. Setting 0 has EP1 + // as bulk. Setting 1 has EP1 as interrupt. The code to handle + // these two EP variations are very similar and can be + // restructured for smaller code footprint. Keeping split out for + // now for clarity. + //TODO add default case. Should it NAK? + // Should likely check the EP of the IN request even if there is + // only one IN endpoint. + // No need to set NAK in OTG_DIEPCTL0 when nothing to send, + // Appears USB core automatically sets NAK. WritePacket clears it. + // Handle the two interface alternate settings. Setting 0 has EP1 + // as bulk. Setting 1 has EP1 as interrupt. The code to handle + // these two EP variations are very similar and can be + // restructured for smaller code footprint. Keeping split out for + // now for clarity. + //TODO add default case. Should it NAK? + switch (current_int0_alt_setting) { + case 0: ////// Bulk config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + USB_WritePacket((void *)response, comms_can_read(response, 0x40), 1); + } + break; + + case 1: ////// Interrupt config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + int len = comms_can_read(response, 0x40); + if (len > 0) { + USB_WritePacket((void *)response, len, 1); + } + } + break; + default: + print("current_int0_alt_setting value invalid\n"); + break; + } + + if ((USBx_INEP(0U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + + if ((ep0_txlen != 0U) && ((USBx_INEP(0U)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= 0x40U)) { + uint16_t len = MIN(ep0_txlen, 0x40); + USB_WritePacket(ep0_txdata, len, 0); + ep0_txdata = &ep0_txdata[len]; + ep0_txlen -= len; + if (ep0_txlen == 0U) { + ep0_txdata = NULL; + USBx_DEVICE->DIEPEMPMSK &= ~1; + USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } + } + } + + // clear interrupts + USBx_INEP(0U)->DIEPINT = USBx_INEP(0U)->DIEPINT; // Why ep0? + USBx_INEP(1U)->DIEPINT = USBx_INEP(1U)->DIEPINT; + } + + // clear all interrupts we handled + USBx_DEVICE->DAINT = daint; + USBx->GOTGINT = gotgint; + USBx->GINTSTS = gintsts; +} + +void can_tx_comms_resume_usb(void) { + ENTER_CRITICAL(); + if (!outep3_processing && (USBx_OUTEP(3U)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != 0U) { + USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; + USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } + EXIT_CRITICAL(); + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); +} diff --git a/board/drivers/usb.h b/board/drivers/usb.h index 47c91c993b9..f56a6d6b29c 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -1,6 +1,7 @@ -#include "board/drivers/drivers.h" +#pragma once // IRQs: OTG_FS +#include typedef union { uint16_t w; @@ -83,796 +84,5 @@ typedef union _USB_Setup { #define ENDPOINT_RCV 0x80 #define ENDPOINT_SND 0x00 -static uint8_t response[USBPACKET_MAX_SIZE]; - -// current packet -static USB_Setup_TypeDef setup; -static uint8_t* ep0_txdata = NULL; -static uint16_t ep0_txlen = 0; -static bool outep3_processing = false; - -// Store the current interface alt setting. -static int current_int0_alt_setting = 0; - -// packet read and write - -static void *USB_ReadPacket(void *dest, uint16_t len) { - uint32_t *dest_copy = (uint32_t *)dest; - uint32_t count32b = ((uint32_t)len + 3U) / 4U; - - for (uint32_t i = 0; i < count32b; i++) { - *dest_copy = USBx_DFIFO(0U); - dest_copy++; - } - return ((void *)dest_copy); -} - -static void USB_WritePacket(const void *src, uint16_t len, uint32_t ep) { - #ifdef DEBUG_USB - print("writing "); - hexdump(src, len); - #endif - - uint32_t numpacket = ((uint32_t)len + (USBPACKET_MAX_SIZE - 1U)) / USBPACKET_MAX_SIZE; - uint32_t count32b = 0; - count32b = ((uint32_t)len + 3U) / 4U; - - // TODO: revisit this - USBx_INEP(ep)->DIEPTSIZ = ((numpacket << 19) & USB_OTG_DIEPTSIZ_PKTCNT) | - (len & USB_OTG_DIEPTSIZ_XFRSIZ); - USBx_INEP(ep)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA); - - // load the FIFO - if (src != NULL) { - const uint32_t *src_copy = (const uint32_t *)src; - for (uint32_t i = 0; i < count32b; i++) { - USBx_DFIFO(ep) = *src_copy; - src_copy++; - } - } -} - -// IN EP 0 TX FIFO has a max size of 127 bytes (much smaller than the rest) -// so use TX FIFO empty interrupt to send larger amounts of data -static void USB_WritePacket_EP0(uint8_t *src, uint16_t len) { - #ifdef DEBUG_USB - print("writing "); - hexdump(src, len); - #endif - - uint16_t wplen = MIN(len, 0x40); - USB_WritePacket(src, wplen, 0); - - if (wplen < len) { - ep0_txdata = &src[wplen]; - ep0_txlen = len - wplen; - USBx_DEVICE->DIEPEMPMSK |= 1; - } else { - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } -} - -static void usb_reset(void) { - // unmask endpoint interrupts, so many sets - USBx_DEVICE->DAINT = 0xFFFFFFFFU; - USBx_DEVICE->DAINTMSK = 0xFFFFFFFFU; - //USBx_DEVICE->DOEPMSK = (USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_EPDM); - //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM | USB_OTG_DIEPMSK_ITTXFEMSK); - //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM); - - // all interrupts for debugging - USBx_DEVICE->DIEPMSK = 0xFFFFFFFFU; - USBx_DEVICE->DOEPMSK = 0xFFFFFFFFU; - - // clear interrupts - USBx_INEP(0U)->DIEPINT = 0xFF; - USBx_OUTEP(0U)->DOEPINT = 0xFF; - - // unset the address - USBx_DEVICE->DCFG &= ~USB_OTG_DCFG_DAD; - - // set up USB FIFOs - // RX start address is fixed to 0 - USBx->GRXFSIZ = 0x40; - - // 0x100 to offset past GRXFSIZ - USBx->DIEPTXF0_HNPTXFSIZ = (0x40UL << 16) | 0x40U; - - // EP1, massive - USBx->DIEPTXF[0] = (0x40UL << 16) | 0x80U; - - // flush TX fifo - USBx->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | USB_OTG_GRSTCTL_TXFNUM_4; - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH); - // flush RX FIFO - USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH); - - // no global NAK - USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGINAK; - - // ready to receive setup packets - USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (3U << 3); -} - -static char to_hex_char(uint8_t a) { - char ret; - if (a < 10U) { - ret = '0' + a; - } else { - ret = 'a' + (a - 10U); - } - return ret; -} - -static void usb_setup(void) { - static uint8_t device_desc[] = { - DSCR_DEVICE_LEN, USB_DESC_TYPE_DEVICE, //Length, Type - 0x10, 0x02, // bcdUSB max version of USB supported (2.1) - 0xFF, 0xFF, 0xFF, 0x40, // Class, Subclass, Protocol, Max Packet Size - TOUSBORDER(USB_VID), // idVendor - TOUSBORDER(USB_PID), // idProduct - 0x00, 0x00, // bcdDevice - 0x01, 0x02, // Manufacturer, Product - 0x03, 0x01 // Serial Number, Num Configurations - }; - - static uint8_t device_qualifier[] = { - 0x0a, USB_DESC_TYPE_DEVICE_QUALIFIER, //Length, Type - 0x10, 0x02, // bcdUSB max version of USB supported (2.1) - 0xFF, 0xFF, 0xFF, 0x40, // bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0 - 0x01, 0x00 // bNumConfigurations, bReserved - }; - - static uint8_t configuration_desc[] = { - DSCR_CONFIG_LEN, USB_DESC_TYPE_CONFIGURATION, // Length, Type, - TOUSBORDER(0x0045U), // Total Len (uint16) - 0x01, 0x01, STRING_OFFSET_ICONFIGURATION, // Num Interface, Config Value, Configuration - 0xc0, 0x32, // Attributes, Max Power - // interface 0 ALT 0 - DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type - 0x00, 0x00, 0x03, // Index, Alt Index idx, Endpoint count - 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol - 0x00, // Interface - // endpoint 1, read CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_RCV | 1, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval (NA) - // endpoint 2, send serial - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - // endpoint 3, send CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - // interface 0 ALT 1 - DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type - 0x00, 0x01, 0x03, // Index, Alt Index idx, Endpoint count - 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol - 0x00, // Interface - // endpoint 1, read CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_RCV | 1, ENDPOINT_TYPE_INT, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x05, // Polling Interval (5 frames) - // endpoint 2, send serial - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - // endpoint 3, send CAN - DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type - ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type - TOUSBORDER(0x0040U), // Max Packet (0x0040) - 0x00, // Polling Interval - }; - - // STRING_DESCRIPTOR_HEADER is for uint16 string descriptors - // it takes in a string length, which is bytes/2 because unicode - static uint16_t string_language_desc[] = { - STRING_DESCRIPTOR_HEADER(1), - 0x0409 // american english - }; - - // these strings are all uint16's so that we don't need to spam ,0 after every character - static uint16_t string_manufacturer_desc[] = { - STRING_DESCRIPTOR_HEADER(8), - 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' - }; - - static uint16_t string_product_desc[] = { - STRING_DESCRIPTOR_HEADER(5), - 'p', 'a', 'n', 'd', 'a' - }; - - // a string containing the default configuration index - static uint16_t string_configuration_desc[] = { - STRING_DESCRIPTOR_HEADER(2), - '0', '1' // "01" - }; - - // WCID (auto install WinUSB driver) - // https://github.com/pbatard/libwdi/wiki/WCID-Devices - // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation#automatic-installation-of--winusb-without-an-inf-file - // WinUSB 1.0 descriptors, this is mostly used by Windows XP - static uint8_t string_238_desc[] = { - 0x12, USB_DESC_TYPE_STRING, // bLength, bDescriptorType - 'M',0, 'S',0, 'F',0, 'T',0, '1',0, '0',0, '0',0, // qwSignature (MSFT100) - MS_VENDOR_CODE, 0x00 // bMS_VendorCode, bPad - }; - - static uint8_t winusb_ext_compatid_os_desc[] = { - 0x28, 0x00, 0x00, 0x00, // dwLength - 0x00, 0x01, // bcdVersion - 0x04, 0x00, // wIndex - 0x01, // bCount - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved - 0x00, // bFirstInterfaceNumber - 0x00, // Reserved - 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subcompatible ID (none) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved - }; - - static uint8_t winusb_ext_prop_os_desc[] = { - 0x8e, 0x00, 0x00, 0x00, // dwLength - 0x00, 0x01, // bcdVersion - 0x05, 0x00, // wIndex - 0x01, 0x00, // wCount - // first property - 0x84, 0x00, 0x00, 0x00, // dwSize - 0x01, 0x00, 0x00, 0x00, // dwPropertyDataType - 0x28, 0x00, // wPropertyNameLength - 'D',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 'I',0, 'n',0, 't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0, 'G',0, 'U',0, 'I',0, 'D',0, 0, 0, // bPropertyName (DeviceInterfaceGUID) - 0x4e, 0x00, 0x00, 0x00, // dwPropertyDataLength - '{',0, 'c',0, 'c',0, 'e',0, '5',0, '2',0, '9',0, '1',0, 'c',0, '-',0, 'a',0, '6',0, '9',0, 'f',0, '-',0, '4',0 ,'9',0 ,'9',0 ,'5',0 ,'-',0, 'a',0, '4',0, 'c',0, '2',0, '-',0, '2',0, 'a',0, 'e',0, '5',0, '7',0, 'a',0, '5',0, '1',0, 'a',0, 'd',0, 'e',0, '9',0, '}',0, 0, 0, // bPropertyData ({CCE5291C-A69F-4995-A4C2-2AE57A51ADE9}) - }; - - /* - Binary Object Store descriptor used to expose WebUSB (and more WinUSB) metadata - comments are from the wicg spec - References used: - https://wicg.github.io/webusb/#webusb-platform-capability-descriptor - https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c - https://os.mbed.com/users/larsgk/code/USBDevice_WebUSB/file/1d8a6665d607/WebUSBDevice/ - */ - static uint8_t binary_object_store_desc[] = { - // BOS header - BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header - BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType - 0x39, 0x00, // wTotalLength (LSB, MSB) - 0x02, // bNumDeviceCaps (WebUSB + WinUSB) - - // ------------------------------------------------- - // WebUSB descriptor - // header - 0x18, // bLength, Size of this descriptor. Must be set to 24. - 0x10, // bDescriptorType, DEVICE CAPABILITY descriptor - 0x05, // bDevCapabilityType, PLATFORM capability - 0x00, // bReserved, This field is reserved and shall be set to zero. - - // PlatformCapabilityUUID, Must be set to {3408b638-09a9-47a0-8bfd-a0768815b665}. - 0x38, 0xB6, 0x08, 0x34, - 0xA9, 0x09, 0xA0, 0x47, - 0x8B, 0xFD, 0xA0, 0x76, - 0x88, 0x15, 0xB6, 0x65, - // - - 0x00, 0x01, // bcdVersion, Protocol version supported. Must be set to 0x0100. - WEBUSB_VENDOR_CODE, // bVendorCode, bRequest value used for issuing WebUSB requests. - // there used to be a concept of "allowed origins", but it was removed from the spec - // it was intended to be a security feature, but then the entire security model relies on domain ownership - // https://github.com/WICG/webusb/issues/49 - // other implementations use various other indexed to leverate this no-longer-valid feature. we wont. - // the spec says we *must* reply to index 0x03 with the url, so we'll hint that that's the right index - 0x03, // iLandingPage, URL descriptor index of the device’s landing page. - - // ------------------------------------------------- - // WinUSB descriptor - // header - 0x1C, // Descriptor size (28 bytes) - 0x10, // Descriptor type (Device Capability) - 0x05, // Capability type (Platform) - 0x00, // Reserved - - // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) - // Indicates the device supports the Microsoft OS 2.0 descriptor - 0xDF, 0x60, 0xDD, 0xD8, - 0x89, 0x45, 0xC7, 0x4C, - 0x9C, 0xD2, 0x65, 0x9D, - 0x9E, 0x64, 0x8A, 0x9F, - - 0x00, 0x00, 0x03, 0x06, // Windows version, currently set to 8.1 (0x06030000) - - WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // MS OS 2.0 descriptor size (word) - MS_VENDOR_CODE, 0x00 // vendor code, no alternate enumeration - }; - - // WinUSB 2.0 descriptor. This is what modern systems use - // https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c - // http://janaxelson.com/files/ms_os_20_descriptors.c - // https://books.google.com/books?id=pkefBgAAQBAJ&pg=PA353&lpg=PA353 - static uint8_t winusb_20_desc[WINUSB_PLATFORM_DESCRIPTOR_LENGTH] = { - // Microsoft OS 2.0 descriptor set header (table 10) - 0x0A, 0x00, // Descriptor size (10 bytes) - 0x00, 0x00, // MS OS 2.0 descriptor set header - - 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) - WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // Total size of MS OS 2.0 descriptor set - - // Microsoft OS 2.0 compatible ID descriptor - 0x14, 0x00, // Descriptor size (20 bytes) - 0x03, 0x00, // MS OS 2.0 compatible ID descriptor - 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-compatible ID - - // Registry property descriptor - 0x80, 0x00, // Descriptor size (130 bytes) - 0x04, 0x00, // Registry Property descriptor - 0x01, 0x00, // Strings are null-terminated Unicode - 0x28, 0x00, // Size of Property Name (40 bytes) "DeviceInterfaceGUID" - - // bPropertyName (DeviceInterfaceGUID) - 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, - 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, - 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, - - 0x4E, 0x00, // Size of Property Data (78 bytes) - - // Vendor-defined property data: {CCE5291C-A69F-4995-A4C2-2AE57A51ADE9} - '{', 0x00, 'c', 0x00, 'c', 0x00, 'e', 0x00, '5', 0x00, '2', 0x00, '9', 0x00, '1', 0x00, // 16 - 'c', 0x00, '-', 0x00, 'a', 0x00, '6', 0x00, '9', 0x00, 'f', 0x00, '-', 0x00, '4', 0x00, // 32 - '9', 0x00, '9', 0x00, '5', 0x00, '-', 0x00, 'a', 0x00, '4', 0x00, 'c', 0x00, '2', 0x00, // 48 - '-', 0x00, '2', 0x00, 'a', 0x00, 'e', 0x00, '5', 0x00, '7', 0x00, 'a', 0x00, '5', 0x00, // 64 - '1', 0x00, 'a', 0x00, 'd', 0x00, 'e', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00 // 78 bytes - }; - - int resp_len; - ControlPacket_t control_req; - - // setup packet is ready - switch (setup.b.bRequest) { - case USB_REQ_SET_CONFIGURATION: - // enable other endpoints, has to be here? - USBx_INEP(1U)->DIEPCTL = (0x40U & USB_OTG_DIEPCTL_MPSIZ) | (2UL << 18) | (1UL << 22) | - USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DIEPCTL_USBAEP; - USBx_INEP(1U)->DIEPINT = 0xFF; - - USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; - USBx_OUTEP(2U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | - USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; - USBx_OUTEP(2U)->DOEPINT = 0xFF; - - USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; - USBx_OUTEP(3U)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | - USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; - USBx_OUTEP(3U)->DOEPINT = 0xFF; - - // mark ready to receive - USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_REQ_SET_ADDRESS: - // set now? - USBx_DEVICE->DCFG |= ((setup.b.wValue.w & 0x7fU) << 4); - - #ifdef DEBUG_USB - print(" set address\n"); - #endif - - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - - break; - case USB_REQ_GET_DESCRIPTOR: - switch (setup.b.wValue.bw.lsb) { - case USB_DESC_TYPE_DEVICE: - //print(" writing device descriptor\n"); - - // set bcdDevice to hardware type - device_desc[13] = hw_type; - // setup transfer - USB_WritePacket(device_desc, MIN(sizeof(device_desc), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - - //print("D"); - break; - case USB_DESC_TYPE_CONFIGURATION: - USB_WritePacket(configuration_desc, MIN(sizeof(configuration_desc), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_DESC_TYPE_DEVICE_QUALIFIER: - USB_WritePacket(device_qualifier, MIN(sizeof(device_qualifier), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_DESC_TYPE_STRING: - switch (setup.b.wValue.bw.msb) { - case STRING_OFFSET_LANGID: - USB_WritePacket((uint8_t*)string_language_desc, MIN(sizeof(string_language_desc), setup.b.wLength.w), 0); - break; - case STRING_OFFSET_IMANUFACTURER: - USB_WritePacket((uint8_t*)string_manufacturer_desc, MIN(sizeof(string_manufacturer_desc), setup.b.wLength.w), 0); - break; - case STRING_OFFSET_IPRODUCT: - USB_WritePacket((uint8_t*)string_product_desc, MIN(sizeof(string_product_desc), setup.b.wLength.w), 0); - break; - case STRING_OFFSET_ISERIAL: - response[0] = 0x02 + (12 * 4); - response[1] = 0x03; - - // 96 bits = 12 bytes - for (int i = 0; i < 12; i++){ - uint8_t cc = ((uint8_t *)UID_BASE)[i]; - response[2 + (i * 4)] = to_hex_char((cc >> 4) & 0xFU); - response[2 + (i * 4) + 1] = '\0'; - response[2 + (i * 4) + 2] = to_hex_char((cc >> 0) & 0xFU); - response[2 + (i * 4) + 3] = '\0'; - } - - USB_WritePacket(response, MIN(response[0], setup.b.wLength.w), 0); - break; - case STRING_OFFSET_ICONFIGURATION: - USB_WritePacket((uint8_t*)string_configuration_desc, MIN(sizeof(string_configuration_desc), setup.b.wLength.w), 0); - break; - case 238: - USB_WritePacket((uint8_t*)string_238_desc, MIN(sizeof(string_238_desc), setup.b.wLength.w), 0); - break; - default: - // nothing - USB_WritePacket(0, 0, 0); - break; - } - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_DESC_TYPE_BINARY_OBJECT_STORE: - USB_WritePacket(binary_object_store_desc, MIN(sizeof(binary_object_store_desc), setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - default: - // nothing here? - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - } - break; - case USB_REQ_GET_STATUS: - // empty response? - response[0] = 0; - response[1] = 0; - USB_WritePacket((void*)&response, 2, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case USB_REQ_SET_INTERFACE: - // Store the alt setting number for IN EP behavior. - current_int0_alt_setting = setup.b.wValue.w; - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case WEBUSB_VENDOR_CODE: - // probably asking for allowed origins, which was removed from the spec - USB_WritePacket(0, 0, 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - break; - case MS_VENDOR_CODE: - switch (setup.b.wIndex.w) { - // winusb 2.0 descriptor from BOS - case WINUSB_REQ_GET_DESCRIPTOR: - USB_WritePacket_EP0((uint8_t*)winusb_20_desc, MIN(sizeof(winusb_20_desc), setup.b.wLength.w)); - break; - // Extended Compat ID OS Descriptor - case WINUSB_REQ_GET_COMPATID_DESCRIPTOR: - USB_WritePacket_EP0((uint8_t*)winusb_ext_compatid_os_desc, MIN(sizeof(winusb_ext_compatid_os_desc), setup.b.wLength.w)); - break; - // Extended Properties OS Descriptor - case WINUSB_REQ_GET_EXT_PROPS_OS: - USB_WritePacket_EP0((uint8_t*)winusb_ext_prop_os_desc, MIN(sizeof(winusb_ext_prop_os_desc), setup.b.wLength.w)); - break; - default: - USB_WritePacket_EP0(0, 0); - } - break; - default: - control_req.request = setup.b.bRequest; - control_req.param1 = setup.b.wValue.w; - control_req.param2 = setup.b.wIndex.w; - control_req.length = setup.b.wLength.w; - - resp_len = comms_control_handler(&control_req, response); - USB_WritePacket(response, MIN(resp_len, setup.b.wLength.w), 0); - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } -} - - - -// ***************************** USB port ***************************** - -void usb_irqhandler(void) { - //USBx->GINTMSK = 0; - static uint8_t usbdata[0x100] __attribute__((aligned(4))); - unsigned int gintsts = USBx->GINTSTS; - unsigned int gotgint = USBx->GOTGINT; - unsigned int daint = USBx_DEVICE->DAINT; - - // gintsts SUSPEND? 04008428 - #ifdef DEBUG_USB - puth(gintsts); - print(" "); - /*puth(USBx->GCCFG); - print(" ");*/ - puth(gotgint); - print(" ep "); - puth(daint); - print(" USB interrupt!\n"); - #endif - - if ((gintsts & USB_OTG_GINTSTS_CIDSCHG) != 0U) { - print("connector ID status change\n"); - } - - if ((gintsts & USB_OTG_GINTSTS_USBRST) != 0U) { - #ifdef DEBUG_USB - print("USB reset\n"); - #endif - usb_reset(); - } - - if ((gintsts & USB_OTG_GINTSTS_ENUMDNE) != 0U) { - #ifdef DEBUG_USB - print("enumeration done\n"); - #endif - // Full speed, ENUMSPD - //puth(USBx_DEVICE->DSTS); - } - - if ((gintsts & USB_OTG_GINTSTS_OTGINT) != 0U) { - #ifdef DEBUG_USB - print("OTG int:"); - puth(USBx->GOTGINT); - print("\n"); - #endif - - // getting ADTOCHG - //USBx->GOTGINT = USBx->GOTGINT; - } - - // RX FIFO first - if ((gintsts & USB_OTG_GINTSTS_RXFLVL) != 0U) { - // 1. Read the Receive status pop register - volatile unsigned int rxst = USBx->GRXSTSP; - int status = (rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17; - - #ifdef DEBUG_USB - print(" RX FIFO:"); - puth(rxst); - print(" status: "); - puth(status); - print(" len: "); - puth((rxst & USB_OTG_GRXSTSP_BCNT) >> 4); - print("\n"); - #endif - - if (status == STS_DATA_UPDT) { - int endpoint = (rxst & USB_OTG_GRXSTSP_EPNUM); - int len = (rxst & USB_OTG_GRXSTSP_BCNT) >> 4; - (void)USB_ReadPacket(&usbdata, len); - #ifdef DEBUG_USB - print(" data "); - puth(len); - print("\n"); - hexdump(&usbdata, len); - #endif - - if (endpoint == 2) { - comms_endpoint2_write((uint8_t *) usbdata, len); - } - - if (endpoint == 3) { - outep3_processing = true; - comms_can_write(usbdata, len); - } - } else if (status == STS_SETUP_UPDT) { - (void)USB_ReadPacket(&setup, 8); - #ifdef DEBUG_USB - print(" setup "); - hexdump(&setup, 8); - print("\n"); - #endif - } else { - // status is neither STS_DATA_UPDT or STS_SETUP_UPDT, skip - } - } - - /*if (gintsts & USB_OTG_GINTSTS_HPRTINT) { - // host - print("HPRT:"); - puth(USBx_HOST_PORT->HPRT); - print("\n"); - if (USBx_HOST_PORT->HPRT & USB_OTG_HPRT_PCDET) { - USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PRST; - USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PCDET; - } - - }*/ - - if ((gintsts & USB_OTG_GINTSTS_BOUTNAKEFF) || (gintsts & USB_OTG_GINTSTS_GINAKEFF)) { - // no global NAK, why is this getting set? - #ifdef DEBUG_USB - print("GLOBAL NAK\n"); - #endif - USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK | USB_OTG_DCTL_CGINAK; - } - - if ((gintsts & USB_OTG_GINTSTS_SRQINT) != 0U) { - // we want to do "A-device host negotiation protocol" since we are the A-device - /*print("start request\n"); - puth(USBx->GOTGCTL); - print("\n");*/ - //USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; - //USBx_HOST_PORT->HPRT = USB_OTG_HPRT_PPWR | USB_OTG_HPRT_PENA; - //USBx->GOTGCTL |= USB_OTG_GOTGCTL_SRQ; - } - - // out endpoint hit - if ((gintsts & USB_OTG_GINTSTS_OEPINT) != 0U) { - #ifdef DEBUG_USB - print(" 0:"); - puth(USBx_OUTEP(0U)->DOEPINT); - print(" 2:"); - puth(USBx_OUTEP(2U)->DOEPINT); - print(" 3:"); - puth(USBx_OUTEP(3U)->DOEPINT); - print(" "); - puth(USBx_OUTEP(3U)->DOEPCTL); - print(" 4:"); - puth(USBx_OUTEP(4)->DOEPINT); - print(" OUT ENDPOINT\n"); - #endif - - if ((USBx_OUTEP(2U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { - #ifdef DEBUG_USB - print(" OUT2 PACKET XFRC\n"); - #endif - USBx_OUTEP(2U)->DOEPTSIZ = (1UL << 19) | 0x40U; - USBx_OUTEP(2U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - } - - if ((USBx_OUTEP(3U)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0U) { - #ifdef DEBUG_USB - print(" OUT3 PACKET XFRC\n"); - #endif - // NAK cleared by process_can (if tx buffers have room) - outep3_processing = false; - refresh_can_tx_slots_available(); - } else if ((USBx_OUTEP(3U)->DOEPINT & 0x2000U) != 0U) { - #ifdef DEBUG_USB - print(" OUT3 PACKET WTF\n"); - #endif - // if NAK was set trigger this, unknown interrupt - // TODO: why was this here? fires when TX buffers when we can't clear NAK - // USBx_OUTEP(3U)->DOEPTSIZ = (1U << 19) | 0x40U; - // USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } else if ((USBx_OUTEP(3U)->DOEPINT) != 0U) { - #ifdef DEBUG_USB - print("OUTEP3 error "); - puth(USBx_OUTEP(3U)->DOEPINT); - print("\n"); - #endif - } else { - // USBx_OUTEP(3U)->DOEPINT is 0, ok to skip - } - - if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DIEPINT_XFRC) != 0U) { - // ready for next packet - USBx_OUTEP(0U)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (1U << 3); - } - - // respond to setup packets - if ((USBx_OUTEP(0U)->DOEPINT & USB_OTG_DOEPINT_STUP) != 0U) { - usb_setup(); - } - - USBx_OUTEP(0U)->DOEPINT = USBx_OUTEP(0U)->DOEPINT; - USBx_OUTEP(2U)->DOEPINT = USBx_OUTEP(2U)->DOEPINT; - USBx_OUTEP(3U)->DOEPINT = USBx_OUTEP(3U)->DOEPINT; - } - - // interrupt endpoint hit (Page 1221) - if ((gintsts & USB_OTG_GINTSTS_IEPINT) != 0U) { - #ifdef DEBUG_USB - print(" "); - puth(USBx_INEP(0U)->DIEPINT); - print(" "); - puth(USBx_INEP(1U)->DIEPINT); - print(" IN ENDPOINT\n"); - #endif - - // Should likely check the EP of the IN request even if there is - // only one IN endpoint. - - // No need to set NAK in OTG_DIEPCTL0 when nothing to send, - // Appears USB core automatically sets NAK. WritePacket clears it. - - // Handle the two interface alternate settings. Setting 0 has EP1 - // as bulk. Setting 1 has EP1 as interrupt. The code to handle - // these two EP variations are very similar and can be - // restructured for smaller code footprint. Keeping split out for - // now for clarity. - - //TODO add default case. Should it NAK? - switch (current_int0_alt_setting) { - case 0: ////// Bulk config - // *** IN token received when TxFIFO is empty - if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { - #ifdef DEBUG_USB - print(" IN PACKET QUEUE\n"); - #endif - // TODO: always assuming max len, can we get the length? - USB_WritePacket((void *)response, comms_can_read(response, 0x40), 1); - } - break; - - case 1: ////// Interrupt config - // *** IN token received when TxFIFO is empty - if ((USBx_INEP(1U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { - #ifdef DEBUG_USB - print(" IN PACKET QUEUE\n"); - #endif - // TODO: always assuming max len, can we get the length? - int len = comms_can_read(response, 0x40); - if (len > 0) { - USB_WritePacket((void *)response, len, 1); - } - } - break; - default: - print("current_int0_alt_setting value invalid\n"); - break; - } - - if ((USBx_INEP(0U)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0U) { - #ifdef DEBUG_USB - print(" IN PACKET QUEUE\n"); - #endif - - if ((ep0_txlen != 0U) && ((USBx_INEP(0U)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= 0x40U)) { - uint16_t len = MIN(ep0_txlen, 0x40); - USB_WritePacket(ep0_txdata, len, 0); - ep0_txdata = &ep0_txdata[len]; - ep0_txlen -= len; - if (ep0_txlen == 0U) { - ep0_txdata = NULL; - USBx_DEVICE->DIEPEMPMSK &= ~1; - USBx_OUTEP(0U)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - } - } - } - - // clear interrupts - USBx_INEP(0U)->DIEPINT = USBx_INEP(0U)->DIEPINT; // Why ep0? - USBx_INEP(1U)->DIEPINT = USBx_INEP(1U)->DIEPINT; - } - - // clear all interrupts we handled - USBx_DEVICE->DAINT = daint; - USBx->GOTGINT = gotgint; - USBx->GINTSTS = gintsts; - - //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); -} - -void can_tx_comms_resume_usb(void) { - ENTER_CRITICAL(); - if (!outep3_processing && (USBx_OUTEP(3U)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != 0U) { - USBx_OUTEP(3U)->DOEPTSIZ = (32UL << 19) | 0x800U; - USBx_OUTEP(3U)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - } - EXIT_CRITICAL(); -} +void usb_init(void); +void usb_irqhandler(void); diff --git a/board/early_init.c b/board/early_init.c new file mode 100644 index 00000000000..85a95b0134b --- /dev/null +++ b/board/early_init.c @@ -0,0 +1,66 @@ +#include "board/config.h" +#include "board/early_init.h" +#include "board/drivers/led.h" +#include "board/sys/critical.h" +#include "board/drivers/registers.h" +#include "board/drivers/interrupts.h" +#include "board/drivers/gpio.h" +#include "board/stm32h7/peripherals.h" + +extern void *g_pfnVectors; + +void detect_board_type(void); + +static void jump_to_bootloader(void) { + // do enter bootloader + enter_bootloader_mode = 0; + + bootloader_fcn_ptr bootloader_ptr = (bootloader_fcn_ptr)BOOTLOADER_ADDRESS; + bootloader_fcn bootloader = *bootloader_ptr; + + // jump to bootloader + enable_interrupts(); + bootloader(); + + // reset on exit + enter_bootloader_mode = BOOT_NORMAL; + NVIC_SystemReset(); +} + +void early_initialization(void) { + // Reset global critical depth + disable_interrupts(); + global_critical_depth = 0; + + // Init register and interrupt tables + init_registers(); + + // after it's been in the bootloader, things are initted differently, so we reset + if ((enter_bootloader_mode != BOOT_NORMAL) && + (enter_bootloader_mode != ENTER_BOOTLOADER_MAGIC) && + (enter_bootloader_mode != ENTER_SOFTLOADER_MAGIC)) { + enter_bootloader_mode = BOOT_NORMAL; + NVIC_SystemReset(); + } + + // if wrong chip, reboot + volatile unsigned int id = DBGMCU->IDCODE; + if ((id & 0xFFFU) != MCU_IDCODE) { + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + } + + // setup interrupt table + SCB->VTOR = (uint32_t)&g_pfnVectors; + + // early GPIOs float everything + early_gpio_float(); + + detect_board_type(); + + if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) { + led_init(); + current_board->init_bootloader(); + led_set(LED_GREEN, 1); + jump_to_bootloader(); + } +} diff --git a/board/early_init.h b/board/early_init.h index d5131a42cfb..2cd20217482 100644 --- a/board/early_init.h +++ b/board/early_init.h @@ -1,64 +1,15 @@ +#pragma once + +#include + // Early bringup #define ENTER_BOOTLOADER_MAGIC 0xdeadbeefU #define ENTER_SOFTLOADER_MAGIC 0xdeadc0deU #define BOOT_NORMAL 0xdeadb111U -extern void *g_pfnVectors; extern uint32_t enter_bootloader_mode; typedef void (*bootloader_fcn)(void); typedef bootloader_fcn *bootloader_fcn_ptr; -static void jump_to_bootloader(void) { - // do enter bootloader - enter_bootloader_mode = 0; - - bootloader_fcn_ptr bootloader_ptr = (bootloader_fcn_ptr)BOOTLOADER_ADDRESS; - bootloader_fcn bootloader = *bootloader_ptr; - - // jump to bootloader - enable_interrupts(); - bootloader(); - - // reset on exit - enter_bootloader_mode = BOOT_NORMAL; - NVIC_SystemReset(); -} - -void early_initialization(void) { - // Reset global critical depth - disable_interrupts(); - global_critical_depth = 0; - - // Init register and interrupt tables - init_registers(); - - // after it's been in the bootloader, things are initted differently, so we reset - if ((enter_bootloader_mode != BOOT_NORMAL) && - (enter_bootloader_mode != ENTER_BOOTLOADER_MAGIC) && - (enter_bootloader_mode != ENTER_SOFTLOADER_MAGIC)) { - enter_bootloader_mode = BOOT_NORMAL; - NVIC_SystemReset(); - } - - // if wrong chip, reboot - volatile unsigned int id = DBGMCU->IDCODE; - if ((id & 0xFFFU) != MCU_IDCODE) { - enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; - } - - // setup interrupt table - SCB->VTOR = (uint32_t)&g_pfnVectors; - - // early GPIOs float everything - early_gpio_float(); - - detect_board_type(); - - if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) { - led_init(); - current_board->init_bootloader(); - led_set(LED_GREEN, 1); - jump_to_bootloader(); - } -} +void early_initialization(void); diff --git a/board/fake_stm.h b/board/fake_stm.h index febf0544b76..61106c53426 100644 --- a/board/fake_stm.h +++ b/board/fake_stm.h @@ -1,4 +1,6 @@ // minimal code to fake a panda for tests +#pragma once + #include #include #include @@ -10,11 +12,11 @@ #define ENTER_CRITICAL() 0 #define EXIT_CRITICAL() 0 -void print(const char *a) { +static inline void print(const char *a) { printf("%s", a); } -void puth(unsigned int i) { +static inline void puth(unsigned int i) { printf("%u", i); } @@ -22,11 +24,10 @@ typedef struct { uint32_t CNT; } TIM_TypeDef; -TIM_TypeDef timer; -TIM_TypeDef *MICROSECOND_TIMER = &timer; -uint32_t microsecond_timer_get(void); +static TIM_TypeDef timer; +static TIM_TypeDef *MICROSECOND_TIMER = &timer; -uint32_t microsecond_timer_get(void) { +static inline uint32_t microsecond_timer_get(void) { return MICROSECOND_TIMER->CNT; } diff --git a/board/flasher.c b/board/flasher.c new file mode 100644 index 00000000000..53aeeca43b4 --- /dev/null +++ b/board/flasher.c @@ -0,0 +1,157 @@ +#include "board/config.h" +#include "board/flasher.h" +#include "board/early_init.h" +#include "board/comms.h" +#include "board/provision.h" +#include "board/drivers/led.h" +#include "board/drivers/usb.h" +#include "board/drivers/spi.h" +#include "board/drivers/gpio.h" +#include "board/stm32h7/peripherals.h" +#include "board/obj/gitversion.h" + +// flasher state variables +static uint32_t *prog_ptr = NULL; +static bool unlocked = false; + +int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { + int resp_len = 0; + + // flasher machine + memset(resp, 0, 4); + memcpy(resp+4, "\xde\xad\xd0\x0d", 4); + resp[0] = 0xff; + resp[2] = req->request; + resp[3] = ~req->request; + *((uint32_t **)&resp[8]) = prog_ptr; + resp_len = 0xc; + + int sec; + switch (req->request) { + // **** 0xb0: flasher echo + case 0xb0: + resp[1] = 0xff; + break; + // **** 0xb1: unlock flash + case 0xb1: + if (flash_is_locked()) { + flash_unlock(); + resp[1] = 0xff; + } + led_set(LED_GREEN, 1); + unlocked = true; + prog_ptr = (uint32_t *)APP_START_ADDRESS; + break; + // **** 0xb2: erase sector + case 0xb2: + sec = req->param1; + if (flash_erase_sector(sec, unlocked)) { + resp[1] = 0xff; + } + break; + // **** 0xc1: get hardware type + case 0xc1: + resp[0] = hw_type; + resp_len = 1; + break; + // **** 0xc3: fetch MCU UID + case 0xc3: + (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); + resp_len = 12; + break; + // **** 0xd0: fetch serial number + case 0xd0: + // addresses are OTP + if (req->param1 == 1) { + memcpy(resp, (void *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); + resp_len = 0x10; + } else { + get_provision_chunk(resp); + resp_len = PROVISION_CHUNK_LEN; + } + break; + // **** 0xd1: enter bootloader mode + case 0xd1: + // this allows reflashing of the bootstub + switch (req->param1) { + case 0: + print("-> entering bootloader\n"); + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + NVIC_SystemReset(); + break; + case 1: + print("-> entering softloader\n"); + enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; + NVIC_SystemReset(); + break; + } + break; + // **** 0xd6: get version + case 0xd6: + COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); + (void)memcpy(resp, gitversion, sizeof(gitversion)); + resp_len = sizeof(gitversion) - 1U; + break; + // **** 0xd8: reset ST + case 0xd8: + flush_write_buffer(); + NVIC_SystemReset(); + break; + } + return resp_len; +} + +void comms_can_write(const uint8_t *data, uint32_t len) { + UNUSED(data); + UNUSED(len); +} + +int comms_can_read(uint8_t *data, uint32_t max_len) { + UNUSED(data); + UNUSED(max_len); + return 0; +} + +void refresh_can_tx_slots_available(void) {} + +void comms_endpoint2_write(const uint8_t *data, uint32_t len) { + led_set(LED_RED, 0); + for (uint32_t i = 0; i < len/4; i++) { + flash_write_word(prog_ptr, *(uint32_t*)(data+(i*4))); + //*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr; + prog_ptr++; + } + led_set(LED_RED, 1); +} + +void soft_flasher_start(void) { + print("\n\n\n************************ FLASHER START ************************\n"); + + enter_bootloader_mode = 0; + + flasher_peripherals_init(); + + gpio_usart2_init(); + gpio_usb_init(); + led_init(); + + // enable comms + usb_init(); + if (current_board->has_spi) { + gpio_spi_init(); + spi_init(); + } + + // green LED on for flashing + led_set(LED_GREEN, 1); + + enable_interrupts(); + + for (;;) { + // blink the green LED fast + led_set(LED_GREEN, 0); + delay(500000); + led_set(LED_GREEN, 1); + delay(500000); + } +} diff --git a/board/flasher.h b/board/flasher.h index 587f7e6269e..360f6fb11d2 100644 --- a/board/flasher.h +++ b/board/flasher.h @@ -1,150 +1,8 @@ -// from the linker script -#define APP_START_ADDRESS 0x8020000U - -// flasher state variables -uint32_t *prog_ptr = NULL; -bool unlocked = false; - -int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { - int resp_len = 0; - - // flasher machine - memset(resp, 0, 4); - memcpy(resp+4, "\xde\xad\xd0\x0d", 4); - resp[0] = 0xff; - resp[2] = req->request; - resp[3] = ~req->request; - *((uint32_t **)&resp[8]) = prog_ptr; - resp_len = 0xc; - - int sec; - switch (req->request) { - // **** 0xb0: flasher echo - case 0xb0: - resp[1] = 0xff; - break; - // **** 0xb1: unlock flash - case 0xb1: - if (flash_is_locked()) { - flash_unlock(); - resp[1] = 0xff; - } - led_set(LED_GREEN, 1); - unlocked = true; - prog_ptr = (uint32_t *)APP_START_ADDRESS; - break; - // **** 0xb2: erase sector - case 0xb2: - sec = req->param1; - if (flash_erase_sector(sec, unlocked)) { - resp[1] = 0xff; - } - break; - // **** 0xc1: get hardware type - case 0xc1: - resp[0] = hw_type; - resp_len = 1; - break; - // **** 0xc3: fetch MCU UID - case 0xc3: - (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); - resp_len = 12; - break; - // **** 0xd0: fetch serial number - case 0xd0: - // addresses are OTP - if (req->param1 == 1) { - memcpy(resp, (void *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); - resp_len = 0x10; - } else { - get_provision_chunk(resp); - resp_len = PROVISION_CHUNK_LEN; - } - break; - // **** 0xd1: enter bootloader mode - case 0xd1: - // this allows reflashing of the bootstub - switch (req->param1) { - case 0: - print("-> entering bootloader\n"); - enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; - NVIC_SystemReset(); - break; - case 1: - print("-> entering softloader\n"); - enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; - NVIC_SystemReset(); - break; - } - break; - // **** 0xd6: get version - case 0xd6: - COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); - memcpy(resp, gitversion, sizeof(gitversion)); - resp_len = sizeof(gitversion) - 1U; - break; - // **** 0xd8: reset ST - case 0xd8: - flush_write_buffer(); - NVIC_SystemReset(); - break; - } - return resp_len; -} - -void comms_can_write(const uint8_t *data, uint32_t len) { - UNUSED(data); - UNUSED(len); -} - -int comms_can_read(uint8_t *data, uint32_t max_len) { - UNUSED(data); - UNUSED(max_len); - return 0; -} - -void refresh_can_tx_slots_available(void) {} - -void comms_endpoint2_write(const uint8_t *data, uint32_t len) { - led_set(LED_RED, 0); - for (uint32_t i = 0; i < len/4; i++) { - flash_write_word(prog_ptr, *(uint32_t*)(data+(i*4))); +#pragma once - //*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr; - prog_ptr++; - } - led_set(LED_RED, 1); -} +#include +// from the linker script +#define APP_START_ADDRESS 0x8020000U -void soft_flasher_start(void) { - print("\n\n\n************************ FLASHER START ************************\n"); - - enter_bootloader_mode = 0; - - flasher_peripherals_init(); - - gpio_usart2_init(); - gpio_usb_init(); - led_init(); - - // enable comms - usb_init(); - if (current_board->has_spi) { - gpio_spi_init(); - spi_init(); - } - - // green LED on for flashing - led_set(LED_GREEN, 1); - - enable_interrupts(); - - for (;;) { - // blink the green LED fast - led_set(LED_GREEN, 0); - delay(500000); - led_set(LED_GREEN, 1); - delay(500000); - } -} +void soft_flasher_start(void); diff --git a/board/jungle/boards/board_declarations.h b/board/jungle/boards/board_declarations.h index aad54dede70..bf981774ae0 100644 --- a/board/jungle/boards/board_declarations.h +++ b/board/jungle/boards/board_declarations.h @@ -1,3 +1,5 @@ +#pragma once + // ******************** Prototypes ******************** typedef void (*board_init)(void); typedef void (*board_board_tick)(void); @@ -55,6 +57,8 @@ struct board { #define SBU2 1U // ********************* Globals ********************** -uint8_t harness_orientation = HARNESS_ORIENTATION_NONE; -uint8_t can_mode = CAN_MODE_NORMAL; -uint8_t ignition = 0U; +extern uint8_t harness_orientation; +extern uint8_t can_mode; +extern uint8_t ignition; + +extern struct board board_v2; diff --git a/board/jungle/boards/board_v2.c b/board/jungle/boards/board_v2.c new file mode 100644 index 00000000000..f2bf8036266 --- /dev/null +++ b/board/jungle/boards/board_v2.c @@ -0,0 +1,311 @@ +#include "board/config.h" +#include "board/jungle/boards/board_v2.h" + +// ///////////////////////// // +// Jungle board v2 (STM32H7) // +// ///////////////////////// // + +#define ADC_CHANNEL(a, c) {.adc = (a), .channel = (c), .sample_time = SAMPLETIME_810_CYCLES, .oversampling = OVERSAMPLING_1} + +static gpio_t power_pins[] = { + {.bank = GPIOA, .pin = 0}, + {.bank = GPIOA, .pin = 1}, + {.bank = GPIOF, .pin = 12}, + {.bank = GPIOA, .pin = 5}, + {.bank = GPIOC, .pin = 5}, + {.bank = GPIOB, .pin = 2}, +}; + +static gpio_t sbu1_ignition_pins[] = { + {.bank = GPIOD, .pin = 0}, + {.bank = GPIOD, .pin = 5}, + {.bank = GPIOD, .pin = 12}, + {.bank = GPIOD, .pin = 14}, + {.bank = GPIOE, .pin = 5}, + {.bank = GPIOE, .pin = 9}, +}; + +static gpio_t sbu1_relay_pins[] = { + {.bank = GPIOD, .pin = 1}, + {.bank = GPIOD, .pin = 6}, + {.bank = GPIOD, .pin = 11}, + {.bank = GPIOD, .pin = 15}, + {.bank = GPIOE, .pin = 6}, + {.bank = GPIOE, .pin = 10}, +}; + +static gpio_t sbu2_ignition_pins[] = { + {.bank = GPIOD, .pin = 3}, + {.bank = GPIOD, .pin = 8}, + {.bank = GPIOD, .pin = 9}, + {.bank = GPIOE, .pin = 0}, + {.bank = GPIOE, .pin = 7}, + {.bank = GPIOE, .pin = 11}, +}; + +static gpio_t sbu2_relay_pins[] = { + {.bank = GPIOD, .pin = 4}, + {.bank = GPIOD, .pin = 10}, + {.bank = GPIOD, .pin = 13}, + {.bank = GPIOE, .pin = 1}, + {.bank = GPIOE, .pin = 8}, + {.bank = GPIOE, .pin = 12}, +}; + +static const adc_signal_t sbu1_channels[] = { + ADC_CHANNEL(ADC3, 12), + ADC_CHANNEL(ADC3, 2), + ADC_CHANNEL(ADC3, 4), + ADC_CHANNEL(ADC3, 6), + ADC_CHANNEL(ADC3, 8), + ADC_CHANNEL(ADC3, 10), +}; + +static const adc_signal_t sbu2_channels[] = { + ADC_CHANNEL(ADC1, 13), + ADC_CHANNEL(ADC3, 3), + ADC_CHANNEL(ADC3, 5), + ADC_CHANNEL(ADC3, 7), + ADC_CHANNEL(ADC3, 9), + ADC_CHANNEL(ADC3, 11), +}; + +void board_v2_set_harness_orientation(uint8_t orientation) { + switch (orientation) { + case HARNESS_ORIENTATION_NONE: + gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false); + harness_orientation = orientation; + break; + case HARNESS_ORIENTATION_1: + gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), true); + gpio_set_bitmask(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), ignition); + gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false); + harness_orientation = orientation; + break; + case HARNESS_ORIENTATION_2: + gpio_set_bitmask(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), ignition); + gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), true); + harness_orientation = orientation; + break; + default: + print("Tried to set an unsupported harness orientation: "); puth(orientation); print("\n"); + break; + } +} + +static void board_v2_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver) { + case 1U: + set_gpio_output(GPIOG, 11, !enabled); + break; + case 2U: + set_gpio_output(GPIOB, 3, !enabled); + break; + case 3U: + set_gpio_output(GPIOD, 7, !enabled); + break; + case 4U: + set_gpio_output(GPIOB, 4, !enabled); + break; + default: + print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n"); + break; + } +} + +static void board_v2_enable_header_pin(uint8_t pin_num, bool enabled) { + if (pin_num < 8U) { + set_gpio_output(GPIOG, pin_num, enabled); + } else { + print("Invalid pin number ("); puth(pin_num); print("): enabling failed\n"); + } +} + +static void board_v2_set_can_mode(uint8_t mode) { + board_v2_enable_can_transceiver(2U, false); + board_v2_enable_can_transceiver(4U, false); + switch (mode) { + case CAN_MODE_NORMAL: + // B12,B13: disable normal mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_mode(GPIOB, 12, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_mode(GPIOB, 13, MODE_ANALOG); + + // B5,B6: FDCAN2 mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); + can_mode = CAN_MODE_NORMAL; + board_v2_enable_can_transceiver(2U, true); + break; + case CAN_MODE_OBD_CAN2: + // B5,B6: disable normal mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_mode(GPIOB, 5, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_mode(GPIOB, 6, MODE_ANALOG); + // B12,B13: FDCAN2 mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); + can_mode = CAN_MODE_OBD_CAN2; + board_v2_enable_can_transceiver(4U, true); + break; + default: + break; + } +} + +bool panda_power = false; +static uint8_t panda_power_bitmask = 0U; +static void board_v2_set_panda_power(bool enable) { + panda_power = enable; + gpio_set_all_output(power_pins, sizeof(power_pins) / sizeof(gpio_t), enable); + if (enable) { + panda_power_bitmask = 0xFFU; + } else { + panda_power_bitmask = 0U; + } +} + +static void board_v2_set_panda_individual_power(uint8_t port_num, bool enable) { + port_num -= 1U; + if (port_num < 6U) { + panda_power_bitmask &= ~(1U << port_num); + panda_power_bitmask |= (enable ? 1U : 0U) << port_num; + } else { + print("Invalid port number ("); puth(port_num); print("): enabling failed\n"); + } + gpio_set_bitmask(power_pins, sizeof(power_pins) / sizeof(gpio_t), (uint32_t)panda_power_bitmask); +} + +static bool board_v2_get_button(void) { + return get_gpio_input(GPIOG, 15); +} + +static void board_v2_set_ignition(bool enabled) { + ignition = enabled ? 0xFFU : 0U; + board_v2_set_harness_orientation(harness_orientation); +} + +static void board_v2_set_individual_ignition(uint8_t bitmask) { + ignition = bitmask; + board_v2_set_harness_orientation(harness_orientation); +} + +static float board_v2_get_channel_power(uint8_t channel) { + float ret = 0.0f; + if ((channel >= 1U) && (channel <= 6U)) { + uint16_t readout = adc_get_mV(&(const adc_signal_t) ADC_CHANNEL(ADC1, channel - 1U)); // these are mapped nicely in hardware + ret = (((float) readout / 33e6) - 0.8e-6) / 52e-6 * 12.0f; + } else { + print("Invalid channel ("); puth(channel); print(")\n"); + } + return ret; +} + +static uint16_t board_v2_get_sbu_mV(uint8_t channel, uint8_t sbu) { + uint16_t ret = 0U; + if ((channel >= 1U) && (channel <= 6U)) { + switch(sbu){ + case SBU1: + ret = adc_get_mV(&sbu1_channels[channel - 1U]); + break; + case SBU2: + ret = adc_get_mV(&sbu2_channels[channel - 1U]); + break; + default: + print("Invalid SBU ("); puth(sbu); print(")\n"); + break; + } + } else { + print("Invalid channel ("); puth(channel); print(")\n"); + } + return ret; +} + +static void board_v2_init(void) { + common_init_gpio(); + + // Normal CAN mode + board_v2_set_can_mode(CAN_MODE_NORMAL); + + // Enable CAN transceivers + for(uint8_t i = 1; i <= 4; i++) { + board_v2_enable_can_transceiver(i, true); + } + + // Set to no harness orientation + board_v2_set_harness_orientation(HARNESS_ORIENTATION_NONE); + + // Enable panda power by default + board_v2_set_panda_power(true); + + // Current monitor channels + adc_init(ADC1); + register_set_bits(&SYSCFG->PMCR, SYSCFG_PMCR_PA0SO | SYSCFG_PMCR_PA1SO); // open up analog switches for PA0_C and PA1_C + set_gpio_mode(GPIOF, 11, MODE_ANALOG); + set_gpio_mode(GPIOA, 6, MODE_ANALOG); + set_gpio_mode(GPIOC, 4, MODE_ANALOG); + set_gpio_mode(GPIOB, 1, MODE_ANALOG); + + // SBU channels + adc_init(ADC3); + set_gpio_mode(GPIOC, 2, MODE_ANALOG); + set_gpio_mode(GPIOC, 3, MODE_ANALOG); + set_gpio_mode(GPIOF, 9, MODE_ANALOG); + set_gpio_mode(GPIOF, 7, MODE_ANALOG); + set_gpio_mode(GPIOF, 5, MODE_ANALOG); + set_gpio_mode(GPIOF, 3, MODE_ANALOG); + set_gpio_mode(GPIOF, 10, MODE_ANALOG); + set_gpio_mode(GPIOF, 8, MODE_ANALOG); + set_gpio_mode(GPIOF, 6, MODE_ANALOG); + set_gpio_mode(GPIOF, 4, MODE_ANALOG); + set_gpio_mode(GPIOC, 0, MODE_ANALOG); + set_gpio_mode(GPIOC, 1, MODE_ANALOG); + + // Header pins + set_gpio_mode(GPIOG, 0, MODE_OUTPUT); + set_gpio_mode(GPIOG, 1, MODE_OUTPUT); + set_gpio_mode(GPIOG, 2, MODE_OUTPUT); + set_gpio_mode(GPIOG, 3, MODE_OUTPUT); + set_gpio_mode(GPIOG, 4, MODE_OUTPUT); + set_gpio_mode(GPIOG, 5, MODE_OUTPUT); + set_gpio_mode(GPIOG, 6, MODE_OUTPUT); + set_gpio_mode(GPIOG, 7, MODE_OUTPUT); +} + +static void board_v2_tick(void) {} + +board board_v2 = { + .avdd_mV = 3300U, + .init = &board_v2_init, + .init_bootloader = &board_v2_tick, + .led_GPIO = {GPIOE, GPIOE, GPIOE}, + .led_pin = {4, 3, 2}, + .board_tick = &board_v2_tick, + .get_button = &board_v2_get_button, + .set_panda_power = &board_v2_set_panda_power, + .set_panda_individual_power = &board_v2_set_panda_individual_power, + .set_ignition = &board_v2_set_ignition, + .set_individual_ignition = &board_v2_set_individual_ignition, + .set_harness_orientation = &board_v2_set_harness_orientation, + .set_can_mode = &board_v2_set_can_mode, + .enable_can_transceiver = &board_v2_enable_can_transceiver, + .enable_header_pin = &board_v2_enable_header_pin, + .get_channel_power = &board_v2_get_channel_power, + .get_sbu_mV = &board_v2_get_sbu_mV, +}; diff --git a/board/jungle/boards/board_v2.h b/board/jungle/boards/board_v2.h index 45f8fb92797..38c0c412575 100644 --- a/board/jungle/boards/board_v2.h +++ b/board/jungle/boards/board_v2.h @@ -1,308 +1,5 @@ -// ///////////////////////// // -// Jungle board v2 (STM32H7) // -// ///////////////////////// // +#pragma once -#define ADC_CHANNEL(a, c) {.adc = (a), .channel = (c), .sample_time = SAMPLETIME_810_CYCLES, .oversampling = OVERSAMPLING_1} +#include "board_declarations.h" -gpio_t power_pins[] = { - {.bank = GPIOA, .pin = 0}, - {.bank = GPIOA, .pin = 1}, - {.bank = GPIOF, .pin = 12}, - {.bank = GPIOA, .pin = 5}, - {.bank = GPIOC, .pin = 5}, - {.bank = GPIOB, .pin = 2}, -}; - -gpio_t sbu1_ignition_pins[] = { - {.bank = GPIOD, .pin = 0}, - {.bank = GPIOD, .pin = 5}, - {.bank = GPIOD, .pin = 12}, - {.bank = GPIOD, .pin = 14}, - {.bank = GPIOE, .pin = 5}, - {.bank = GPIOE, .pin = 9}, -}; - -gpio_t sbu1_relay_pins[] = { - {.bank = GPIOD, .pin = 1}, - {.bank = GPIOD, .pin = 6}, - {.bank = GPIOD, .pin = 11}, - {.bank = GPIOD, .pin = 15}, - {.bank = GPIOE, .pin = 6}, - {.bank = GPIOE, .pin = 10}, -}; - -gpio_t sbu2_ignition_pins[] = { - {.bank = GPIOD, .pin = 3}, - {.bank = GPIOD, .pin = 8}, - {.bank = GPIOD, .pin = 9}, - {.bank = GPIOE, .pin = 0}, - {.bank = GPIOE, .pin = 7}, - {.bank = GPIOE, .pin = 11}, -}; - -gpio_t sbu2_relay_pins[] = { - {.bank = GPIOD, .pin = 4}, - {.bank = GPIOD, .pin = 10}, - {.bank = GPIOD, .pin = 13}, - {.bank = GPIOE, .pin = 1}, - {.bank = GPIOE, .pin = 8}, - {.bank = GPIOE, .pin = 12}, -}; - -const adc_signal_t sbu1_channels[] = { - ADC_CHANNEL(ADC3, 12), - ADC_CHANNEL(ADC3, 2), - ADC_CHANNEL(ADC3, 4), - ADC_CHANNEL(ADC3, 6), - ADC_CHANNEL(ADC3, 8), - ADC_CHANNEL(ADC3, 10), -}; - -const adc_signal_t sbu2_channels[] = { - ADC_CHANNEL(ADC1, 13), - ADC_CHANNEL(ADC3, 3), - ADC_CHANNEL(ADC3, 5), - ADC_CHANNEL(ADC3, 7), - ADC_CHANNEL(ADC3, 9), - ADC_CHANNEL(ADC3, 11), -}; - -void board_v2_set_harness_orientation(uint8_t orientation) { - switch (orientation) { - case HARNESS_ORIENTATION_NONE: - gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false); - gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false); - gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false); - gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false); - harness_orientation = orientation; - break; - case HARNESS_ORIENTATION_1: - gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false); - gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), true); - gpio_set_bitmask(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), ignition); - gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false); - harness_orientation = orientation; - break; - case HARNESS_ORIENTATION_2: - gpio_set_bitmask(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), ignition); - gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false); - gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false); - gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), true); - harness_orientation = orientation; - break; - default: - print("Tried to set an unsupported harness orientation: "); puth(orientation); print("\n"); - break; - } -} - -void board_v2_enable_can_transceiver(uint8_t transceiver, bool enabled) { - switch (transceiver) { - case 1U: - set_gpio_output(GPIOG, 11, !enabled); - break; - case 2U: - set_gpio_output(GPIOB, 3, !enabled); - break; - case 3U: - set_gpio_output(GPIOD, 7, !enabled); - break; - case 4U: - set_gpio_output(GPIOB, 4, !enabled); - break; - default: - print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n"); - break; - } -} - -void board_v2_enable_header_pin(uint8_t pin_num, bool enabled) { - if (pin_num < 8U) { - set_gpio_output(GPIOG, pin_num, enabled); - } else { - print("Invalid pin number ("); puth(pin_num); print("): enabling failed\n"); - } -} - -void board_v2_set_can_mode(uint8_t mode) { - board_v2_enable_can_transceiver(2U, false); - board_v2_enable_can_transceiver(4U, false); - switch (mode) { - case CAN_MODE_NORMAL: - // B12,B13: disable normal mode - set_gpio_pullup(GPIOB, 12, PULL_NONE); - set_gpio_mode(GPIOB, 12, MODE_ANALOG); - - set_gpio_pullup(GPIOB, 13, PULL_NONE); - set_gpio_mode(GPIOB, 13, MODE_ANALOG); - - // B5,B6: FDCAN2 mode - set_gpio_pullup(GPIOB, 5, PULL_NONE); - set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); - - set_gpio_pullup(GPIOB, 6, PULL_NONE); - set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); - can_mode = CAN_MODE_NORMAL; - board_v2_enable_can_transceiver(2U, true); - break; - case CAN_MODE_OBD_CAN2: - // B5,B6: disable normal mode - set_gpio_pullup(GPIOB, 5, PULL_NONE); - set_gpio_mode(GPIOB, 5, MODE_ANALOG); - - set_gpio_pullup(GPIOB, 6, PULL_NONE); - set_gpio_mode(GPIOB, 6, MODE_ANALOG); - // B12,B13: FDCAN2 mode - set_gpio_pullup(GPIOB, 12, PULL_NONE); - set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2); - - set_gpio_pullup(GPIOB, 13, PULL_NONE); - set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); - can_mode = CAN_MODE_OBD_CAN2; - board_v2_enable_can_transceiver(4U, true); - break; - default: - break; - } -} - -bool panda_power = false; -uint8_t panda_power_bitmask = 0U; -void board_v2_set_panda_power(bool enable) { - panda_power = enable; - gpio_set_all_output(power_pins, sizeof(power_pins) / sizeof(gpio_t), enable); - if (enable) { - panda_power_bitmask = 0xFFU; - } else { - panda_power_bitmask = 0U; - } -} - -void board_v2_set_panda_individual_power(uint8_t port_num, bool enable) { - port_num -= 1U; - if (port_num < 6U) { - panda_power_bitmask &= ~(1U << port_num); - panda_power_bitmask |= (enable ? 1U : 0U) << port_num; - } else { - print("Invalid port number ("); puth(port_num); print("): enabling failed\n"); - } - gpio_set_bitmask(power_pins, sizeof(power_pins) / sizeof(gpio_t), (uint32_t)panda_power_bitmask); -} - -bool board_v2_get_button(void) { - return get_gpio_input(GPIOG, 15); -} - -void board_v2_set_ignition(bool enabled) { - ignition = enabled ? 0xFFU : 0U; - board_v2_set_harness_orientation(harness_orientation); -} - -void board_v2_set_individual_ignition(uint8_t bitmask) { - ignition = bitmask; - board_v2_set_harness_orientation(harness_orientation); -} - -float board_v2_get_channel_power(uint8_t channel) { - float ret = 0.0f; - if ((channel >= 1U) && (channel <= 6U)) { - uint16_t readout = adc_get_mV(&(const adc_signal_t) ADC_CHANNEL(ADC1, channel - 1U)); // these are mapped nicely in hardware - ret = (((float) readout / 33e6) - 0.8e-6) / 52e-6 * 12.0f; - } else { - print("Invalid channel ("); puth(channel); print(")\n"); - } - return ret; -} - -uint16_t board_v2_get_sbu_mV(uint8_t channel, uint8_t sbu) { - uint16_t ret = 0U; - if ((channel >= 1U) && (channel <= 6U)) { - switch(sbu){ - case SBU1: - ret = adc_get_mV(&sbu1_channels[channel - 1U]); - break; - case SBU2: - ret = adc_get_mV(&sbu2_channels[channel - 1U]); - break; - default: - print("Invalid SBU ("); puth(sbu); print(")\n"); - break; - } - } else { - print("Invalid channel ("); puth(channel); print(")\n"); - } - return ret; -} - -void board_v2_init(void) { - common_init_gpio(); - - // Normal CAN mode - board_v2_set_can_mode(CAN_MODE_NORMAL); - - // Enable CAN transceivers - for(uint8_t i = 1; i <= 4; i++) { - board_v2_enable_can_transceiver(i, true); - } - - // Set to no harness orientation - board_v2_set_harness_orientation(HARNESS_ORIENTATION_NONE); - - // Enable panda power by default - board_v2_set_panda_power(true); - - // Current monitor channels - adc_init(ADC1); - register_set_bits(&SYSCFG->PMCR, SYSCFG_PMCR_PA0SO | SYSCFG_PMCR_PA1SO); // open up analog switches for PA0_C and PA1_C - set_gpio_mode(GPIOF, 11, MODE_ANALOG); - set_gpio_mode(GPIOA, 6, MODE_ANALOG); - set_gpio_mode(GPIOC, 4, MODE_ANALOG); - set_gpio_mode(GPIOB, 1, MODE_ANALOG); - - // SBU channels - adc_init(ADC3); - set_gpio_mode(GPIOC, 2, MODE_ANALOG); - set_gpio_mode(GPIOC, 3, MODE_ANALOG); - set_gpio_mode(GPIOF, 9, MODE_ANALOG); - set_gpio_mode(GPIOF, 7, MODE_ANALOG); - set_gpio_mode(GPIOF, 5, MODE_ANALOG); - set_gpio_mode(GPIOF, 3, MODE_ANALOG); - set_gpio_mode(GPIOF, 10, MODE_ANALOG); - set_gpio_mode(GPIOF, 8, MODE_ANALOG); - set_gpio_mode(GPIOF, 6, MODE_ANALOG); - set_gpio_mode(GPIOF, 4, MODE_ANALOG); - set_gpio_mode(GPIOC, 0, MODE_ANALOG); - set_gpio_mode(GPIOC, 1, MODE_ANALOG); - - // Header pins - set_gpio_mode(GPIOG, 0, MODE_OUTPUT); - set_gpio_mode(GPIOG, 1, MODE_OUTPUT); - set_gpio_mode(GPIOG, 2, MODE_OUTPUT); - set_gpio_mode(GPIOG, 3, MODE_OUTPUT); - set_gpio_mode(GPIOG, 4, MODE_OUTPUT); - set_gpio_mode(GPIOG, 5, MODE_OUTPUT); - set_gpio_mode(GPIOG, 6, MODE_OUTPUT); - set_gpio_mode(GPIOG, 7, MODE_OUTPUT); -} - -void board_v2_tick(void) {} - -board board_v2 = { - .avdd_mV = 3300U, - .init = &board_v2_init, - .init_bootloader = &board_v2_tick, - .led_GPIO = {GPIOE, GPIOE, GPIOE}, - .led_pin = {4, 3, 2}, - .board_tick = &board_v2_tick, - .get_button = &board_v2_get_button, - .set_panda_power = &board_v2_set_panda_power, - .set_panda_individual_power = &board_v2_set_panda_individual_power, - .set_ignition = &board_v2_set_ignition, - .set_individual_ignition = &board_v2_set_individual_ignition, - .set_harness_orientation = &board_v2_set_harness_orientation, - .set_can_mode = &board_v2_set_can_mode, - .enable_can_transceiver = &board_v2_enable_can_transceiver, - .enable_header_pin = &board_v2_enable_header_pin, - .get_channel_power = &board_v2_get_channel_power, - .get_sbu_mV = &board_v2_get_sbu_mV, -}; +extern bool panda_power; diff --git a/board/jungle/main.c b/board/jungle/main.c index 4ef9950666d..57f1236a362 100644 --- a/board/jungle/main.c +++ b/board/jungle/main.c @@ -1,6 +1,8 @@ // ********************* Includes ********************* #include "board/config.h" +#include "board/jungle/stm32h7/board.h" + #include "opendbc/safety/safety.h" #include "board/drivers/led.h" diff --git a/board/jungle/main_comms.c b/board/jungle/main_comms.c new file mode 100644 index 00000000000..529670aaaef --- /dev/null +++ b/board/jungle/main_comms.c @@ -0,0 +1,269 @@ +#include "board/config.h" +#include "board/early_init.h" +#include "board/health.h" +#include "board/jungle/jungle_health.h" +#include "board/jungle/main_comms.h" +#include "board/obj/gitversion.h" +#include "board/provision.h" + +extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used + +bool generated_can_traffic = false; + +static int get_jungle_health_pkt(void *dat) { + COMPILE_TIME_ASSERT(sizeof(struct jungle_health_t) <= USBPACKET_MAX_SIZE); + struct jungle_health_t * health = (struct jungle_health_t*)dat; + + health->uptime_pkt = uptime_cnt; + health->ch1_power = current_board->get_channel_power(1U); + health->ch2_power = current_board->get_channel_power(2U); + health->ch3_power = current_board->get_channel_power(3U); + health->ch4_power = current_board->get_channel_power(4U); + health->ch5_power = current_board->get_channel_power(5U); + health->ch6_power = current_board->get_channel_power(6U); + + health->ch1_sbu1_mV = current_board->get_sbu_mV(1U, SBU1); + health->ch1_sbu2_mV = current_board->get_sbu_mV(1U, SBU2); + health->ch2_sbu1_mV = current_board->get_sbu_mV(2U, SBU1); + health->ch2_sbu2_mV = current_board->get_sbu_mV(2U, SBU2); + health->ch3_sbu1_mV = current_board->get_sbu_mV(3U, SBU1); + health->ch3_sbu2_mV = current_board->get_sbu_mV(3U, SBU2); + health->ch4_sbu1_mV = current_board->get_sbu_mV(4U, SBU1); + health->ch4_sbu2_mV = current_board->get_sbu_mV(4U, SBU2); + health->ch5_sbu1_mV = current_board->get_sbu_mV(5U, SBU1); + health->ch5_sbu2_mV = current_board->get_sbu_mV(5U, SBU2); + health->ch6_sbu1_mV = current_board->get_sbu_mV(6U, SBU1); + health->ch6_sbu2_mV = current_board->get_sbu_mV(6U, SBU2); + + return sizeof(*health); +} + +// send on serial, first byte to select the ring +void comms_endpoint2_write(const uint8_t *data, uint32_t len) { + UNUSED(data); + UNUSED(len); +} + +int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { + unsigned int resp_len = 0; + uint32_t time; + +#ifdef DEBUG_COMMS + print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n"); + print("- request "); puth(req->request); print("\n"); + print("- param1 "); puth(req->param1); print("\n"); + print("- param2 "); puth(req->param2); print("\n"); +#endif + + switch (req->request) { + // **** 0xa0: Set panda power. + case 0xa0: + current_board->set_panda_power((req->param1 == 1U)); + break; + // **** 0xa1: Set harness orientation. + case 0xa1: + current_board->set_harness_orientation(req->param1); + break; + // **** 0xa2: Set ignition. + case 0xa2: + current_board->set_ignition((req->param1 == 1U)); + break; + // **** 0xa3: Set panda power per channel by bitmask. + case 0xa3: + current_board->set_panda_individual_power(req->param1, (req->param2 > 0U)); + break; + // **** 0xa4: Enable generated CAN traffic. + case 0xa4: + generated_can_traffic = (req->param1 > 0U); + break; + // **** 0xa8: get microsecond timer + case 0xa8: + time = microsecond_timer_get(); + resp[0] = (time & 0x000000FFU); + resp[1] = ((time & 0x0000FF00U) >> 8U); + resp[2] = ((time & 0x00FF0000U) >> 16U); + resp[3] = ((time & 0xFF000000U) >> 24U); + resp_len = 4U; + break; + // **** 0xc0: reset communications + case 0xc0: + comms_can_reset(); + break; + // **** 0xc1: get hardware type + case 0xc1: + resp[0] = hw_type; + resp_len = 1; + break; + // **** 0xc2: CAN health stats + case 0xc2: + COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE); + if (req->param1 < 3U) { + update_can_health_pkt(req->param1, 0U); + can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U); + can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U); + can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled; + can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled; + can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso; + resp_len = sizeof(can_health[req->param1]); + (void)memcpy(resp, &can_health[req->param1], resp_len); + } + break; + // **** 0xc3: fetch MCU UID + case 0xc3: + (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); + resp_len = 12; + break; + // **** 0xd0: fetch serial (aka the provisioned dongle ID) + case 0xd0: + // addresses are OTP + if (req->param1 == 1U) { + (void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); + resp_len = 0x10; + } else { + get_provision_chunk(resp); + resp_len = PROVISION_CHUNK_LEN; + } + break; + // **** 0xd1: enter bootloader mode + case 0xd1: + // this allows reflashing of the bootstub + switch (req->param1) { + case 0: + // only allow bootloader entry on debug builds + #ifdef ALLOW_DEBUG + print("-> entering bootloader\n"); + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + NVIC_SystemReset(); + #endif + break; + case 1: + print("-> entering softloader\n"); + enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; + NVIC_SystemReset(); + break; + default: + print("Bootloader mode invalid\n"); + break; + } + break; + // **** 0xd2: get health packet + case 0xd2: + resp_len = get_jungle_health_pkt(resp); + break; + // **** 0xd3: get first 64 bytes of signature + case 0xd3: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len], resp_len); + } + break; + // **** 0xd4: get second 64 bytes of signature + case 0xd4: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len + 64], resp_len); + } + break; + // **** 0xd6: get version + case 0xd6: + COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); + (void)memcpy(resp, gitversion, sizeof(gitversion)); + resp_len = sizeof(gitversion) - 1U; + break; + // **** 0xd8: reset ST + case 0xd8: + NVIC_SystemReset(); + break; + // **** 0xdb: set OBD CAN multiplexing mode + case 0xdb: + if (req->param1 == 1U) { + // Enable OBD CAN + current_board->set_can_mode(CAN_MODE_OBD_CAN2); + } else { + // Disable OBD CAN + current_board->set_can_mode(CAN_MODE_NORMAL); + } + break; + // **** 0xdd: get healthpacket and CANPacket version hashes + case 0xdd: { + uint32_t versions[2] = {JUNGLE_HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH}; + (void)memcpy(resp, (uint8_t *)versions, sizeof(versions)); + resp_len = sizeof(versions); + break; + } + // **** 0xde: set can bitrate + case 0xde: + if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { + bus_config[req->param1].can_speed = req->param2; + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xe0: debug read + case 0xe0: + // read + while ((resp_len < MIN(req->length, USBPACKET_MAX_SIZE)) && get_char(get_ring_by_number(0), (char*)&resp[resp_len])) { + ++resp_len; + } + break; + // **** 0xe5: set CAN loopback (for testing) + case 0xe5: + can_loopback = (req->param1 > 0U); + can_init_all(); + break; + // **** 0xf1: Clear CAN ring buffer. + case 0xf1: + if (req->param1 == 0xFFFFU) { + print("Clearing CAN Rx queue\n"); + can_clear(&can_rx_q); + } else if (req->param1 < PANDA_CAN_CNT) { + print("Clearing CAN Tx queue\n"); + can_clear(can_queues[req->param1]); + } else { + print("Clearing CAN CAN ring buffer failed: wrong bus number\n"); + } + break; + // **** 0xf4: Set CAN transceiver enable pin + case 0xf4: + current_board->enable_can_transceiver(req->param1, req->param2 > 0U); + break; + // **** 0xf5: Set CAN silent mode + case 0xf5: + can_silent = (req->param1 > 0U); + can_init_all(); + break; + // **** 0xf7: enable/disable header pin by number + case 0xf7: + current_board->enable_header_pin(req->param1, req->param2 > 0U); + break; + // **** 0xf9: set CAN FD data bitrate + case 0xf9: + if ((req->param1 < PANDA_CAN_CNT) && + is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) { + bus_config[req->param1].can_data_speed = req->param2; + bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed); + bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xfc: set CAN FD non-ISO mode + case 0xfc: + if (req->param1 < PANDA_CAN_CNT) { + bus_config[req->param1].canfd_non_iso = (req->param2 != 0U); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + default: + print("NO HANDLER "); + puth(req->request); + print("\n"); + break; + } + return resp_len; +} diff --git a/board/jungle/main_comms.h b/board/jungle/main_comms.h index 290b7c72fa0..c6819d50d8a 100644 --- a/board/jungle/main_comms.h +++ b/board/jungle/main_comms.h @@ -1,261 +1,5 @@ -extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used +#pragma once -bool generated_can_traffic = false; +#include -int get_jungle_health_pkt(void *dat) { - COMPILE_TIME_ASSERT(sizeof(struct jungle_health_t) <= USBPACKET_MAX_SIZE); - struct jungle_health_t * health = (struct jungle_health_t*)dat; - - health->uptime_pkt = uptime_cnt; - health->ch1_power = current_board->get_channel_power(1U); - health->ch2_power = current_board->get_channel_power(2U); - health->ch3_power = current_board->get_channel_power(3U); - health->ch4_power = current_board->get_channel_power(4U); - health->ch5_power = current_board->get_channel_power(5U); - health->ch6_power = current_board->get_channel_power(6U); - - health->ch1_sbu1_mV = current_board->get_sbu_mV(1U, SBU1); - health->ch1_sbu2_mV = current_board->get_sbu_mV(1U, SBU2); - health->ch2_sbu1_mV = current_board->get_sbu_mV(2U, SBU1); - health->ch2_sbu2_mV = current_board->get_sbu_mV(2U, SBU2); - health->ch3_sbu1_mV = current_board->get_sbu_mV(3U, SBU1); - health->ch3_sbu2_mV = current_board->get_sbu_mV(3U, SBU2); - health->ch4_sbu1_mV = current_board->get_sbu_mV(4U, SBU1); - health->ch4_sbu2_mV = current_board->get_sbu_mV(4U, SBU2); - health->ch5_sbu1_mV = current_board->get_sbu_mV(5U, SBU1); - health->ch5_sbu2_mV = current_board->get_sbu_mV(5U, SBU2); - health->ch6_sbu1_mV = current_board->get_sbu_mV(6U, SBU1); - health->ch6_sbu2_mV = current_board->get_sbu_mV(6U, SBU2); - - return sizeof(*health); -} - -// send on serial, first byte to select the ring -void comms_endpoint2_write(const uint8_t *data, uint32_t len) { - UNUSED(data); - UNUSED(len); -} - -int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { - unsigned int resp_len = 0; - uint32_t time; - -#ifdef DEBUG_COMMS - print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n"); - print("- request "); puth(req->request); print("\n"); - print("- param1 "); puth(req->param1); print("\n"); - print("- param2 "); puth(req->param2); print("\n"); -#endif - - switch (req->request) { - // **** 0xa0: Set panda power. - case 0xa0: - current_board->set_panda_power((req->param1 == 1U)); - break; - // **** 0xa1: Set harness orientation. - case 0xa1: - current_board->set_harness_orientation(req->param1); - break; - // **** 0xa2: Set ignition. - case 0xa2: - current_board->set_ignition((req->param1 == 1U)); - break; - // **** 0xa3: Set panda power per channel by bitmask. - case 0xa3: - current_board->set_panda_individual_power(req->param1, (req->param2 > 0U)); - break; - // **** 0xa4: Enable generated CAN traffic. - case 0xa4: - generated_can_traffic = (req->param1 > 0U); - break; - // **** 0xa8: get microsecond timer - case 0xa8: - time = microsecond_timer_get(); - resp[0] = (time & 0x000000FFU); - resp[1] = ((time & 0x0000FF00U) >> 8U); - resp[2] = ((time & 0x00FF0000U) >> 16U); - resp[3] = ((time & 0xFF000000U) >> 24U); - resp_len = 4U; - break; - // **** 0xc0: reset communications - case 0xc0: - comms_can_reset(); - break; - // **** 0xc1: get hardware type - case 0xc1: - resp[0] = hw_type; - resp_len = 1; - break; - // **** 0xc2: CAN health stats - case 0xc2: - COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE); - if (req->param1 < 3U) { - update_can_health_pkt(req->param1, 0U); - can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U); - can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U); - can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled; - can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled; - can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso; - resp_len = sizeof(can_health[req->param1]); - (void)memcpy(resp, &can_health[req->param1], resp_len); - } - break; - // **** 0xc3: fetch MCU UID - case 0xc3: - (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); - resp_len = 12; - break; - // **** 0xd0: fetch serial (aka the provisioned dongle ID) - case 0xd0: - // addresses are OTP - if (req->param1 == 1U) { - (void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); - resp_len = 0x10; - } else { - get_provision_chunk(resp); - resp_len = PROVISION_CHUNK_LEN; - } - break; - // **** 0xd1: enter bootloader mode - case 0xd1: - // this allows reflashing of the bootstub - switch (req->param1) { - case 0: - // only allow bootloader entry on debug builds - #ifdef ALLOW_DEBUG - print("-> entering bootloader\n"); - enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; - NVIC_SystemReset(); - #endif - break; - case 1: - print("-> entering softloader\n"); - enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; - NVIC_SystemReset(); - break; - default: - print("Bootloader mode invalid\n"); - break; - } - break; - // **** 0xd2: get health packet - case 0xd2: - resp_len = get_jungle_health_pkt(resp); - break; - // **** 0xd3: get first 64 bytes of signature - case 0xd3: - { - resp_len = 64; - char * code = (char*)_app_start; - int code_len = _app_start[0]; - (void)memcpy(resp, &code[code_len], resp_len); - } - break; - // **** 0xd4: get second 64 bytes of signature - case 0xd4: - { - resp_len = 64; - char * code = (char*)_app_start; - int code_len = _app_start[0]; - (void)memcpy(resp, &code[code_len + 64], resp_len); - } - break; - // **** 0xd6: get version - case 0xd6: - COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); - (void)memcpy(resp, gitversion, sizeof(gitversion)); - resp_len = sizeof(gitversion) - 1U; - break; - // **** 0xd8: reset ST - case 0xd8: - NVIC_SystemReset(); - break; - // **** 0xdb: set OBD CAN multiplexing mode - case 0xdb: - if (req->param1 == 1U) { - // Enable OBD CAN - current_board->set_can_mode(CAN_MODE_OBD_CAN2); - } else { - // Disable OBD CAN - current_board->set_can_mode(CAN_MODE_NORMAL); - } - break; - // **** 0xdd: get healthpacket and CANPacket version hashes - case 0xdd: { - uint32_t versions[2] = {JUNGLE_HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH}; - (void)memcpy(resp, (uint8_t *)versions, sizeof(versions)); - resp_len = sizeof(versions); - break; - } - // **** 0xde: set can bitrate - case 0xde: - if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { - bus_config[req->param1].can_speed = req->param2; - bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); - UNUSED(ret); - } - break; - // **** 0xe0: debug read - case 0xe0: - // read - while ((resp_len < MIN(req->length, USBPACKET_MAX_SIZE)) && get_char(get_ring_by_number(0), (char*)&resp[resp_len])) { - ++resp_len; - } - break; - // **** 0xe5: set CAN loopback (for testing) - case 0xe5: - can_loopback = (req->param1 > 0U); - can_init_all(); - break; - // **** 0xf1: Clear CAN ring buffer. - case 0xf1: - if (req->param1 == 0xFFFFU) { - print("Clearing CAN Rx queue\n"); - can_clear(&can_rx_q); - } else if (req->param1 < PANDA_CAN_CNT) { - print("Clearing CAN Tx queue\n"); - can_clear(can_queues[req->param1]); - } else { - print("Clearing CAN CAN ring buffer failed: wrong bus number\n"); - } - break; - // **** 0xf4: Set CAN transceiver enable pin - case 0xf4: - current_board->enable_can_transceiver(req->param1, req->param2 > 0U); - break; - // **** 0xf5: Set CAN silent mode - case 0xf5: - can_silent = (req->param1 > 0U); - can_init_all(); - break; - // **** 0xf7: enable/disable header pin by number - case 0xf7: - current_board->enable_header_pin(req->param1, req->param2 > 0U); - break; - // **** 0xf9: set CAN FD data bitrate - case 0xf9: - if ((req->param1 < PANDA_CAN_CNT) && - is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) { - bus_config[req->param1].can_data_speed = req->param2; - bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed); - bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed); - bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); - UNUSED(ret); - } - break; - // **** 0xfc: set CAN FD non-ISO mode - case 0xfc: - if (req->param1 < PANDA_CAN_CNT) { - bus_config[req->param1].canfd_non_iso = (req->param2 != 0U); - bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); - UNUSED(ret); - } - break; - default: - print("NO HANDLER "); - puth(req->request); - print("\n"); - break; - } - return resp_len; -} +extern bool generated_can_traffic; diff --git a/board/jungle/stm32h7/board.c b/board/jungle/stm32h7/board.c new file mode 100644 index 00000000000..75ce20d9fe0 --- /dev/null +++ b/board/jungle/stm32h7/board.c @@ -0,0 +1,6 @@ +#include "board/config.h" + +void detect_board_type(void) { + hw_type = HW_TYPE_V2; + current_board = &board_v2; +} diff --git a/board/jungle/stm32h7/board.h b/board/jungle/stm32h7/board.h index 760a70416ac..36f69eccf61 100644 --- a/board/jungle/stm32h7/board.h +++ b/board/jungle/stm32h7/board.h @@ -1,9 +1,6 @@ -#include "board/jungle/boards/board_declarations.h" +#pragma once -#include "board/stm32h7/lladc.h" +#include "board/jungle/boards/board_declarations.h" #include "board/jungle/boards/board_v2.h" -void detect_board_type(void) { - hw_type = HW_TYPE_V2; - current_board = &board_v2; -} +void detect_board_type(void); diff --git a/board/libc.c b/board/libc.c new file mode 100644 index 00000000000..80a1111b805 --- /dev/null +++ b/board/libc.c @@ -0,0 +1,81 @@ +// **** libc **** + +#include "board/libc.h" + +#define UNALIGNED(X, Y) \ + (((uint32_t)(X) & (sizeof(uint32_t) - 1U)) | ((uint32_t)(Y) & (sizeof(uint32_t) - 1U))) + +__attribute__((aligned(32), noinline)) void delay(uint32_t a) { + // loop is 2.6x faster when 32-byte aligned (ART accelerator prefetches flash in 32-byte chunks) + volatile uint32_t i; + uint32_t n = a * 13U / 5U; + for (i = 0; i < n; i++) {} +} + +void assert_fatal(bool condition, const char *msg) { + if (!condition) { + print("ASSERT FAILED\n"); + print(msg); + while (1) { + // hang + } + } +} + +// cppcheck-suppress misra-c2012-21.2 +void *memset(void *str, int c, unsigned int n) { + uint8_t *s = str; + for (unsigned int i = 0; i < n; i++) { + *s = c; + s++; + } + return str; +} + +// cppcheck-suppress misra-c2012-21.2 +void *memcpy(void *dest, const void *src, unsigned int len) { + unsigned int n = len; + uint8_t *d8 = dest; + const uint8_t *s8 = src; + + if ((n >= 4U) && !UNALIGNED(s8, d8)) { + uint32_t *d32 = (uint32_t *)d8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned + const uint32_t *s32 = (const uint32_t *)s8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned + + while(n >= 16U) { + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + n -= 16U; + } + + while(n >= 4U) { + *d32 = *s32; d32++; s32++; + n -= 4U; + } + + d8 = (uint8_t *)d32; + s8 = (const uint8_t *)s32; + } + while (n-- > 0U) { + *d8 = *s8; d8++; s8++; + } + return dest; +} + +// cppcheck-suppress misra-c2012-21.2 +int memcmp(const void * ptr1, const void * ptr2, unsigned int num) { + int ret = 0; + const uint8_t *p1 = ptr1; + const uint8_t *p2 = ptr2; + for (unsigned int i = 0; i < num; i++) { + if (*p1 != *p2) { + ret = -1; + break; + } + p1++; + p2++; + } + return ret; +} diff --git a/board/libc.h b/board/libc.h index c5ea629fcef..97e340e7830 100644 --- a/board/libc.h +++ b/board/libc.h @@ -1,79 +1,19 @@ -// **** libc **** +#pragma once -__attribute__((aligned(32), noinline)) void delay(uint32_t a) { - // loop is 2.6x faster when 32-byte aligned (ART accelerator prefetches flash in 32-byte chunks) - volatile uint32_t i; - uint32_t n = a * 13U / 5U; - for (i = 0; i < n; i++) {} -} +#include +#include -void assert_fatal(bool condition, const char *msg) { - if (!condition) { - print("ASSERT FAILED\n"); - print(msg); - while (1) { - // hang - } - } -} +// print/puth are defined in uart.c for STM32 builds. +// In test builds, fake_stm.h provides static inline versions via -include. +#ifdef STM32H7 +void print(const char *a); +void puth(unsigned int i); +#endif -// cppcheck-suppress misra-c2012-21.2 -void *memset(void *str, int c, unsigned int n) { - uint8_t *s = str; - for (unsigned int i = 0; i < n; i++) { - *s = c; - s++; - } - return str; -} +__attribute__((aligned(32), noinline)) void delay(uint32_t a); -#define UNALIGNED(X, Y) \ - (((uint32_t)(X) & (sizeof(uint32_t) - 1U)) | ((uint32_t)(Y) & (sizeof(uint32_t) - 1U))) +void assert_fatal(bool condition, const char *msg); -// cppcheck-suppress misra-c2012-21.2 -void *memcpy(void *dest, const void *src, unsigned int len) { - unsigned int n = len; - uint8_t *d8 = dest; - const uint8_t *s8 = src; - - if ((n >= 4U) && !UNALIGNED(s8, d8)) { - uint32_t *d32 = (uint32_t *)d8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned - const uint32_t *s32 = (const uint32_t *)s8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned - - while(n >= 16U) { - *d32 = *s32; d32++; s32++; - *d32 = *s32; d32++; s32++; - *d32 = *s32; d32++; s32++; - *d32 = *s32; d32++; s32++; - n -= 16U; - } - - while(n >= 4U) { - *d32 = *s32; d32++; s32++; - n -= 4U; - } - - d8 = (uint8_t *)d32; - s8 = (const uint8_t *)s32; - } - while (n-- > 0U) { - *d8 = *s8; d8++; s8++; - } - return dest; -} - -// cppcheck-suppress misra-c2012-21.2 -int memcmp(const void * ptr1, const void * ptr2, unsigned int num) { - int ret = 0; - const uint8_t *p1 = ptr1; - const uint8_t *p2 = ptr2; - for (unsigned int i = 0; i < num; i++) { - if (*p1 != *p2) { - ret = -1; - break; - } - p1++; - p2++; - } - return ret; -} +void *memset(void *str, int c, unsigned int n); +void *memcpy(void *dest, const void *src, unsigned int len); +int memcmp(const void * ptr1, const void * ptr2, unsigned int num); diff --git a/board/main.c b/board/main.c index e3e98074798..111e6fad0e4 100644 --- a/board/main.c +++ b/board/main.c @@ -1,6 +1,8 @@ // ********************* Includes ********************* #include "board/config.h" +#include "board/stm32h7/board.h" + #include "board/drivers/led.h" #include "board/drivers/pwm.h" #include "board/drivers/usb.h" diff --git a/board/main_comms.c b/board/main_comms.c new file mode 100644 index 00000000000..a02f85106ab --- /dev/null +++ b/board/main_comms.c @@ -0,0 +1,345 @@ +#include "board/config.h" +#include "board/early_init.h" +#include "board/health.h" +#include "board/main_comms.h" +#include "board/obj/gitversion.h" +#include "board/provision.h" +#include "opendbc/safety/declarations.h" + +extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used + +static int get_health_pkt(void *dat) { + COMPILE_TIME_ASSERT(sizeof(struct health_t) <= USBPACKET_MAX_SIZE); + struct health_t * health = (struct health_t*)dat; + + health->uptime_pkt = uptime_cnt; + health->voltage_pkt = current_board->read_voltage_mV(); + health->current_pkt = current_board->read_current_mA(); + + health->ignition_line_pkt = (uint8_t)(harness_check_ignition()); + health->ignition_can_pkt = ignition_can; + + health->controls_allowed_pkt = controls_allowed; + health->safety_tx_blocked_pkt = safety_tx_blocked; + health->safety_rx_invalid_pkt = safety_rx_invalid; + health->tx_buffer_overflow_pkt = tx_buffer_overflow; + health->rx_buffer_overflow_pkt = rx_buffer_overflow; + health->car_harness_status_pkt = harness.status; + 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_enabled; + health->heartbeat_lost_pkt = heartbeat_lost; + health->safety_rx_checks_invalid_pkt = safety_rx_checks_invalid; + + health->spi_error_count_pkt = spi_error_count; + + health->fault_status_pkt = fault_status; + health->faults_pkt = faults; + + health->interrupt_load_pkt = interrupt_load; + + health->fan_power = fan_state.power; + + health->sbu1_voltage_mV = harness.sbu1_voltage_mV; + health->sbu2_voltage_mV = harness.sbu2_voltage_mV; + + health->som_reset_triggered = bootkick_reset_triggered; + + health->sound_output_level_pkt = sound_output_level; + + return sizeof(*health); +} + +// send on serial, first byte to select the ring +void comms_endpoint2_write(const uint8_t *data, uint32_t len) { + uart_ring *ur = get_ring_by_number(data[0]); + if ((len != 0U) && (ur != NULL)) { + if ((data[0] < 2U) || (data[0] >= 4U)) { + for (uint32_t i = 1; i < len; i++) { + while (!put_char(ur, data[i])) { + // wait + } + } + } + } +} + +int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { + unsigned int resp_len = 0; + uart_ring *ur = NULL; + uint32_t time; + +#ifdef DEBUG_COMMS + print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n"); + print("- request "); puth(req->request); print("\n"); + print("- param1 "); puth(req->param1); print("\n"); + print("- param2 "); puth(req->param2); print("\n"); +#endif + + switch (req->request) { + // **** 0xa8: get microsecond timer + case 0xa8: + time = microsecond_timer_get(); + resp[0] = (time & 0x000000FFU); + resp[1] = ((time & 0x0000FF00U) >> 8U); + resp[2] = ((time & 0x00FF0000U) >> 16U); + resp[3] = ((time & 0xFF000000U) >> 24U); + resp_len = 4U; + break; + // **** 0xb0: set IR power + case 0xb0: + current_board->set_ir_power(req->param1); + break; + // **** 0xb1: set fan power + case 0xb1: + fan_set_power(req->param1); + break; + // **** 0xb2: get fan rpm + case 0xb2: + resp[0] = (fan_state.rpm & 0x00FFU); + 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(); + break; + // **** 0xc1: get hardware type + case 0xc1: + resp[0] = hw_type; + resp_len = 1; + break; + // **** 0xc2: CAN health stats + case 0xc2: + COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE); + if (req->param1 < 3U) { + update_can_health_pkt(req->param1, 0U); + can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U); + can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U); + can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled; + can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled; + can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso; + resp_len = sizeof(can_health[req->param1]); + (void)memcpy(resp, (uint8_t*)(&can_health[req->param1]), resp_len); + } + break; + // **** 0xc3: fetch MCU UID + case 0xc3: + (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); + resp_len = 12; + break; + // **** 0xc4: get interrupt call rate + case 0xc4: + if (req->param1 < NUM_INTERRUPTS) { + uint32_t load = interrupts[req->param1].call_rate; + resp[0] = (load & 0x000000FFU); + resp[1] = ((load & 0x0000FF00U) >> 8U); + resp[2] = ((load & 0x00FF0000U) >> 16U); + resp[3] = ((load & 0xFF000000U) >> 24U); + resp_len = 4U; + } + break; + // **** 0xc5: DEBUG: drive relay + case 0xc5: + set_intercept_relay((req->param1 & 0x1U), (req->param1 & 0x2U)); + break; + // **** 0xc6: DEBUG: read SOM GPIO + case 0xc6: + resp[0] = current_board->read_som_gpio(); + resp_len = 1; + break; + // **** 0xd0: fetch serial (aka the provisioned dongle ID) + case 0xd0: + // addresses are OTP + if (req->param1 == 1U) { + (void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); + resp_len = 0x10; + } else { + get_provision_chunk(resp); + resp_len = PROVISION_CHUNK_LEN; + } + break; + // **** 0xd1: enter bootloader mode + case 0xd1: + // this allows reflashing of the bootstub + switch (req->param1) { + case 0: + // only allow bootloader entry on debug builds + #ifdef ALLOW_DEBUG + print("-> entering bootloader\n"); + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + NVIC_SystemReset(); + #endif + break; + case 1: + print("-> entering softloader\n"); + enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; + NVIC_SystemReset(); + break; + default: + print("Bootloader mode invalid\n"); + break; + } + break; + // **** 0xd2: get health packet + case 0xd2: + resp_len = get_health_pkt(resp); + break; + // **** 0xd3: get first 64 bytes of signature + case 0xd3: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len], resp_len); + } + break; + // **** 0xd4: get second 64 bytes of signature + case 0xd4: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len + 64], resp_len); + } + break; + // **** 0xd6: get version + case 0xd6: + COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); + (void)memcpy(resp, gitversion, sizeof(gitversion)); + resp_len = sizeof(gitversion) - 1U; + break; + // **** 0xd8: reset ST + case 0xd8: + NVIC_SystemReset(); + break; + // **** 0xdb: set OBD CAN multiplexing mode + case 0xdb: + current_board->set_can_mode((req->param1 == 1U) ? CAN_MODE_OBD_CAN2 : CAN_MODE_NORMAL); + break; + // **** 0xdc: set safety mode + case 0xdc: + set_safety_mode(req->param1, (uint16_t)req->param2); + break; + // **** 0xdd: get health and CAN packet versions + case 0xdd: { + uint32_t versions[2] = {HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH}; + (void)memcpy(resp, (uint8_t *)versions, sizeof(versions)); + resp_len = sizeof(versions); + break; + } + // **** 0xde: set can bitrate + case 0xde: + if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { + bus_config[req->param1].can_speed = req->param2; + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xdf: set alternative experience + case 0xdf: + // you can only set this if you are in a non car safety mode + if (!is_car_safety_mode(current_safety_mode)) { + alternative_experience = req->param1; + } + break; + // **** 0xe0: uart read + case 0xe0: + ur = get_ring_by_number(req->param1); + if (!ur) { + break; + } + + // read + { + uint16_t req_length = MIN(req->length, USBPACKET_MAX_SIZE); + while ((resp_len < req_length) && + get_char(ur, (char*)&resp[resp_len])) { + ++resp_len; + } + } + break; + // **** 0xe5: set CAN loopback (for testing) + case 0xe5: + can_loopback = req->param1 > 0U; + can_init_all(); + break; + // **** 0xe6: set custom clock source period and pulse length + case 0xe6: + clock_source_set_timer_params(req->param1, req->param2); + break; + // **** 0xe7: set power save state + case 0xe7: + set_power_save_state(req->param1 != 0U); + break; + // **** 0xe8: set can-fd auto swithing mode + case 0xe8: + bus_config[req->param1].canfd_auto = req->param2 > 0U; + break; + // **** 0xf1: Clear CAN ring buffer. + case 0xf1: + if (req->param1 == 0xFFFFU) { + print("Clearing CAN Rx queue\n"); + can_clear(&can_rx_q); + } else if (req->param1 < PANDA_CAN_CNT) { + print("Clearing CAN Tx queue\n"); + can_clear(can_queues[req->param1]); + } else { + print("Clearing CAN CAN ring buffer failed: wrong bus number\n"); + } + break; + // **** 0xf3: Heartbeat. Resets heartbeat counter. + case 0xf3: + { + heartbeat_counter = 0U; + heartbeat_lost = false; + heartbeat_disabled = false; + heartbeat_engaged = (req->param1 == 1U); + break; + } + // **** 0xf6: set siren enabled + case 0xf6: + siren_enabled = (req->param1 != 0U); + break; + // **** 0xf8: disable heartbeat checks + case 0xf8: + if (!is_car_safety_mode(current_safety_mode)) { + heartbeat_disabled = true; + } + break; + // **** 0xf9: set CAN FD data bitrate + case 0xf9: + if ((req->param1 < PANDA_CAN_CNT) && + is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) { + bus_config[req->param1].can_data_speed = req->param2; + bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed); + bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xfc: set CAN FD non-ISO mode + case 0xfc: + if (req->param1 < PANDA_CAN_CNT) { + bus_config[req->param1].canfd_non_iso = (req->param2 != 0U); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + default: + print("NO HANDLER "); + puth(req->request); + print("\n"); + break; + } + return resp_len; +} diff --git a/board/main_comms.h b/board/main_comms.h index 8229a47ab9a..0d0d234c4aa 100644 --- a/board/main_comms.h +++ b/board/main_comms.h @@ -1,339 +1,8 @@ -extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used +#pragma once + +#include +#include // Prototypes void set_safety_mode(uint16_t mode, uint16_t param); bool is_car_safety_mode(uint16_t mode); - -static int get_health_pkt(void *dat) { - COMPILE_TIME_ASSERT(sizeof(struct health_t) <= USBPACKET_MAX_SIZE); - struct health_t * health = (struct health_t*)dat; - - health->uptime_pkt = uptime_cnt; - health->voltage_pkt = current_board->read_voltage_mV(); - health->current_pkt = current_board->read_current_mA(); - - health->ignition_line_pkt = (uint8_t)(harness_check_ignition()); - health->ignition_can_pkt = ignition_can; - - health->controls_allowed_pkt = controls_allowed; - health->safety_tx_blocked_pkt = safety_tx_blocked; - health->safety_rx_invalid_pkt = safety_rx_invalid; - health->tx_buffer_overflow_pkt = tx_buffer_overflow; - health->rx_buffer_overflow_pkt = rx_buffer_overflow; - health->car_harness_status_pkt = harness.status; - 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_enabled; - health->heartbeat_lost_pkt = heartbeat_lost; - health->safety_rx_checks_invalid_pkt = safety_rx_checks_invalid; - - health->spi_error_count_pkt = spi_error_count; - - health->fault_status_pkt = fault_status; - health->faults_pkt = faults; - - health->interrupt_load_pkt = interrupt_load; - - health->fan_power = fan_state.power; - - health->sbu1_voltage_mV = harness.sbu1_voltage_mV; - health->sbu2_voltage_mV = harness.sbu2_voltage_mV; - - health->som_reset_triggered = bootkick_reset_triggered; - - health->sound_output_level_pkt = sound_output_level; - - return sizeof(*health); -} - -// send on serial, first byte to select the ring -void comms_endpoint2_write(const uint8_t *data, uint32_t len) { - uart_ring *ur = get_ring_by_number(data[0]); - if ((len != 0U) && (ur != NULL)) { - if ((data[0] < 2U) || (data[0] >= 4U)) { - for (uint32_t i = 1; i < len; i++) { - while (!put_char(ur, data[i])) { - // wait - } - } - } - } -} - -int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { - unsigned int resp_len = 0; - uart_ring *ur = NULL; - uint32_t time; - -#ifdef DEBUG_COMMS - print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n"); - print("- request "); puth(req->request); print("\n"); - print("- param1 "); puth(req->param1); print("\n"); - print("- param2 "); puth(req->param2); print("\n"); -#endif - - switch (req->request) { - // **** 0xa8: get microsecond timer - case 0xa8: - time = microsecond_timer_get(); - resp[0] = (time & 0x000000FFU); - resp[1] = ((time & 0x0000FF00U) >> 8U); - resp[2] = ((time & 0x00FF0000U) >> 16U); - resp[3] = ((time & 0xFF000000U) >> 24U); - resp_len = 4U; - break; - // **** 0xb0: set IR power - case 0xb0: - current_board->set_ir_power(req->param1); - break; - // **** 0xb1: set fan power - case 0xb1: - fan_set_power(req->param1); - break; - // **** 0xb2: get fan rpm - case 0xb2: - resp[0] = (fan_state.rpm & 0x00FFU); - 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(); - break; - // **** 0xc1: get hardware type - case 0xc1: - resp[0] = hw_type; - resp_len = 1; - break; - // **** 0xc2: CAN health stats - case 0xc2: - COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE); - if (req->param1 < 3U) { - update_can_health_pkt(req->param1, 0U); - can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U); - can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U); - can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled; - can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled; - can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso; - resp_len = sizeof(can_health[req->param1]); - (void)memcpy(resp, (uint8_t*)(&can_health[req->param1]), resp_len); - } - break; - // **** 0xc3: fetch MCU UID - case 0xc3: - (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); - resp_len = 12; - break; - // **** 0xc4: get interrupt call rate - case 0xc4: - if (req->param1 < NUM_INTERRUPTS) { - uint32_t load = interrupts[req->param1].call_rate; - resp[0] = (load & 0x000000FFU); - resp[1] = ((load & 0x0000FF00U) >> 8U); - resp[2] = ((load & 0x00FF0000U) >> 16U); - resp[3] = ((load & 0xFF000000U) >> 24U); - resp_len = 4U; - } - break; - // **** 0xc5: DEBUG: drive relay - case 0xc5: - set_intercept_relay((req->param1 & 0x1U), (req->param1 & 0x2U)); - break; - // **** 0xc6: DEBUG: read SOM GPIO - case 0xc6: - resp[0] = current_board->read_som_gpio(); - resp_len = 1; - break; - // **** 0xd0: fetch serial (aka the provisioned dongle ID) - case 0xd0: - // addresses are OTP - if (req->param1 == 1U) { - (void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); - resp_len = 0x10; - } else { - get_provision_chunk(resp); - resp_len = PROVISION_CHUNK_LEN; - } - break; - // **** 0xd1: enter bootloader mode - case 0xd1: - // this allows reflashing of the bootstub - switch (req->param1) { - case 0: - // only allow bootloader entry on debug builds - #ifdef ALLOW_DEBUG - print("-> entering bootloader\n"); - enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; - NVIC_SystemReset(); - #endif - break; - case 1: - print("-> entering softloader\n"); - enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; - NVIC_SystemReset(); - break; - default: - print("Bootloader mode invalid\n"); - break; - } - break; - // **** 0xd2: get health packet - case 0xd2: - resp_len = get_health_pkt(resp); - break; - // **** 0xd3: get first 64 bytes of signature - case 0xd3: - { - resp_len = 64; - char * code = (char*)_app_start; - int code_len = _app_start[0]; - (void)memcpy(resp, &code[code_len], resp_len); - } - break; - // **** 0xd4: get second 64 bytes of signature - case 0xd4: - { - resp_len = 64; - char * code = (char*)_app_start; - int code_len = _app_start[0]; - (void)memcpy(resp, &code[code_len + 64], resp_len); - } - break; - // **** 0xd6: get version - case 0xd6: - COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); - (void)memcpy(resp, gitversion, sizeof(gitversion)); - resp_len = sizeof(gitversion) - 1U; - break; - // **** 0xd8: reset ST - case 0xd8: - NVIC_SystemReset(); - break; - // **** 0xdb: set OBD CAN multiplexing mode - case 0xdb: - current_board->set_can_mode((req->param1 == 1U) ? CAN_MODE_OBD_CAN2 : CAN_MODE_NORMAL); - break; - // **** 0xdc: set safety mode - case 0xdc: - set_safety_mode(req->param1, (uint16_t)req->param2); - break; - // **** 0xdd: get health and CAN packet versions - case 0xdd: { - uint32_t versions[2] = {HEALTH_PACKET_VERSION, CAN_PACKET_VERSION_HASH}; - (void)memcpy(resp, (uint8_t *)versions, sizeof(versions)); - resp_len = sizeof(versions); - break; - } - // **** 0xde: set can bitrate - case 0xde: - if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { - bus_config[req->param1].can_speed = req->param2; - bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); - UNUSED(ret); - } - break; - // **** 0xdf: set alternative experience - case 0xdf: - // you can only set this if you are in a non car safety mode - if (!is_car_safety_mode(current_safety_mode)) { - alternative_experience = req->param1; - } - break; - // **** 0xe0: uart read - case 0xe0: - ur = get_ring_by_number(req->param1); - if (!ur) { - break; - } - - // read - uint16_t req_length = MIN(req->length, USBPACKET_MAX_SIZE); - while ((resp_len < req_length) && - get_char(ur, (char*)&resp[resp_len])) { - ++resp_len; - } - break; - // **** 0xe5: set CAN loopback (for testing) - case 0xe5: - can_loopback = req->param1 > 0U; - can_init_all(); - break; - // **** 0xe6: set custom clock source period and pulse length - case 0xe6: - clock_source_set_timer_params(req->param1, req->param2); - break; - // **** 0xe7: set power save state - case 0xe7: - set_power_save_state(req->param1 != 0U); - break; - // **** 0xe8: set can-fd auto swithing mode - case 0xe8: - bus_config[req->param1].canfd_auto = req->param2 > 0U; - break; - // **** 0xf1: Clear CAN ring buffer. - case 0xf1: - if (req->param1 == 0xFFFFU) { - print("Clearing CAN Rx queue\n"); - can_clear(&can_rx_q); - } else if (req->param1 < PANDA_CAN_CNT) { - print("Clearing CAN Tx queue\n"); - can_clear(can_queues[req->param1]); - } else { - print("Clearing CAN CAN ring buffer failed: wrong bus number\n"); - } - break; - // **** 0xf3: Heartbeat. Resets heartbeat counter. - case 0xf3: - { - heartbeat_counter = 0U; - heartbeat_lost = false; - heartbeat_disabled = false; - heartbeat_engaged = (req->param1 == 1U); - break; - } - // **** 0xf6: set siren enabled - case 0xf6: - siren_enabled = (req->param1 != 0U); - break; - // **** 0xf8: disable heartbeat checks - case 0xf8: - if (!is_car_safety_mode(current_safety_mode)) { - heartbeat_disabled = true; - } - break; - // **** 0xf9: set CAN FD data bitrate - case 0xf9: - if ((req->param1 < PANDA_CAN_CNT) && - is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) { - bus_config[req->param1].can_data_speed = req->param2; - bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed); - bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed); - bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); - UNUSED(ret); - } - break; - // **** 0xfc: set CAN FD non-ISO mode - case 0xfc: - if (req->param1 < PANDA_CAN_CNT) { - bus_config[req->param1].canfd_non_iso = (req->param2 != 0U); - bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); - UNUSED(ret); - } - break; - default: - print("NO HANDLER "); - puth(req->request); - print("\n"); - break; - } - return resp_len; -} diff --git a/board/main_definitions.h b/board/main_definitions.h deleted file mode 100644 index 83c91090579..00000000000 --- a/board/main_definitions.h +++ /dev/null @@ -1,14 +0,0 @@ -#include "main_declarations.h" - -// ********************* Globals ********************** -uint8_t hw_type = 0; -board *current_board; -uint32_t uptime_cnt = 0; - -// heartbeat state -uint32_t heartbeat_counter = 0; -bool heartbeat_lost = false; -bool heartbeat_disabled = false; // set over USB - -// siren state -bool siren_enabled = false; diff --git a/board/main_globals.c b/board/main_globals.c new file mode 100644 index 00000000000..1fbbe814bce --- /dev/null +++ b/board/main_globals.c @@ -0,0 +1,19 @@ +#ifndef BOOTSTUB +#include "board/main_globals.h" + +uint8_t hw_type = 0; +board *current_board; +uint32_t uptime_cnt = 0; + +uint32_t heartbeat_counter = 0; +bool heartbeat_lost = false; +bool heartbeat_disabled = false; + +bool siren_enabled = false; + +#ifdef PANDA_JUNGLE +uint8_t harness_orientation = 0U; +uint8_t can_mode = 0U; +uint8_t ignition = 0U; +#endif +#endif diff --git a/board/main_declarations.h b/board/main_globals.h similarity index 52% rename from board/main_declarations.h rename to board/main_globals.h index 19411340e8a..be852db57f9 100644 --- a/board/main_declarations.h +++ b/board/main_globals.h @@ -1,24 +1,19 @@ #pragma once -// ******************** Prototypes ******************** -void print(const char *a); -void puth(unsigned int i); +#include +#include + 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); -// ********************* Globals ********************** extern uint8_t hw_type; extern board *current_board; extern uint32_t uptime_cnt; -// heartbeat state extern uint32_t heartbeat_counter; extern bool heartbeat_lost; extern bool heartbeat_disabled; -// siren state extern bool siren_enabled; // sound diff --git a/board/provision.c b/board/provision.c new file mode 100644 index 00000000000..b706502151d --- /dev/null +++ b/board/provision.c @@ -0,0 +1,17 @@ +#include "board/provision.h" +#include "board/libc.h" + +#ifdef STM32H7 +#define PROVISION_CHUNK_ADDRESS 0x080FFFE0U +#else +#define PROVISION_CHUNK_ADDRESS 0x0U +#endif + +void get_provision_chunk(uint8_t *resp) { + const unsigned char unprovisioned_text[] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; + + (void)memcpy(resp, (uint8_t *)PROVISION_CHUNK_ADDRESS, PROVISION_CHUNK_LEN); + if (memcmp(resp, unprovisioned_text, 0x20) == 0) { + (void)memcpy(resp, "unprovisioned\x00\x00\x00testing123\x00\x00\xa3\xa6\x99\xec", 0x20); + } +} diff --git a/board/provision.h b/board/provision.h index d191e53f677..f8d1f076a68 100644 --- a/board/provision.h +++ b/board/provision.h @@ -1,13 +1,10 @@ +#pragma once + // this is where we manage the dongle ID assigned during our // manufacturing. aside from this, there's a UID for the MCU -#define PROVISION_CHUNK_LEN 0x20 +#include -void get_provision_chunk(uint8_t *resp) { - const unsigned char unprovisioned_text[] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; +#define PROVISION_CHUNK_LEN 0x20 - (void)memcpy(resp, (uint8_t *)PROVISION_CHUNK_ADDRESS, PROVISION_CHUNK_LEN); - if (memcmp(resp, unprovisioned_text, 0x20) == 0) { - (void)memcpy(resp, "unprovisioned\x00\x00\x00testing123\x00\x00\xa3\xa6\x99\xec", 0x20); - } -} +void get_provision_chunk(uint8_t *resp); diff --git a/board/stm32h7/board.c b/board/stm32h7/board.c new file mode 100644 index 00000000000..3480d87b316 --- /dev/null +++ b/board/stm32h7/board.c @@ -0,0 +1,39 @@ +// ///////////////////////////////////////////////////////////// // +// Hardware abstraction layer for all different supported boards // +// ///////////////////////////////////////////////////////////// // + +#include "board/config.h" +#include "board/stm32h7/board.h" +#include "board/drivers/gpio.h" + +// ///// Board definition and detection ///// // +void detect_board_type(void) { + // On STM32H7 pandas, we use two different sets of pins. + const uint8_t id1 = detect_with_pull(GPIOF, 7, PULL_UP) | + (detect_with_pull(GPIOF, 8, PULL_UP) << 1U) | + (detect_with_pull(GPIOF, 9, PULL_UP) << 2U) | + (detect_with_pull(GPIOF, 10, PULL_UP) << 3U); + + const uint8_t id2 = detect_with_pull(GPIOD, 4, PULL_UP) | + (detect_with_pull(GPIOD, 5, PULL_UP) << 1U) | + (detect_with_pull(GPIOD, 6, PULL_UP) << 2U) | + (detect_with_pull(GPIOD, 7, PULL_UP) << 3U); + + if (id2 == 3U) { + hw_type = HW_TYPE_CUATRO; + current_board = &board_cuatro; + } else if (id1 == 0U) { + hw_type = HW_TYPE_RED_PANDA; + current_board = &board_red; + } else if (id1 == 1U) { + // deprecated + //hw_type = HW_TYPE_RED_PANDA_V2; + hw_type = HW_TYPE_UNKNOWN; + } else if (id1 == 2U) { + hw_type = HW_TYPE_TRES; + current_board = &board_tres; + } else { + hw_type = HW_TYPE_UNKNOWN; + print("Hardware type is UNKNOWN!\n"); + } +} diff --git a/board/stm32h7/board.h b/board/stm32h7/board.h index 05c5c5e7155..fa3193f1fac 100644 --- a/board/stm32h7/board.h +++ b/board/stm32h7/board.h @@ -1,49 +1,9 @@ -// ///////////////////////////////////////////////////////////// // -// Hardware abstraction layer for all different supported boards // -// ///////////////////////////////////////////////////////////// // +#pragma once + #include "board/boards/board_declarations.h" #include "board/boards/unused_funcs.h" - -// ///// Board definition and detection ///// // -#include "board/stm32h7/lladc.h" -#include "board/drivers/harness.h" -#include "board/drivers/fan.h" -#include "board/stm32h7/llfan.h" -#include "board/stm32h7/sound.h" -#include "board/drivers/fake_siren.h" -#include "board/drivers/clock_source.h" #include "board/boards/red.h" #include "board/boards/tres.h" #include "board/boards/cuatro.h" - -void detect_board_type(void) { - // On STM32H7 pandas, we use two different sets of pins. - const uint8_t id1 = detect_with_pull(GPIOF, 7, PULL_UP) | - (detect_with_pull(GPIOF, 8, PULL_UP) << 1U) | - (detect_with_pull(GPIOF, 9, PULL_UP) << 2U) | - (detect_with_pull(GPIOF, 10, PULL_UP) << 3U); - - const uint8_t id2 = detect_with_pull(GPIOD, 4, PULL_UP) | - (detect_with_pull(GPIOD, 5, PULL_UP) << 1U) | - (detect_with_pull(GPIOD, 6, PULL_UP) << 2U) | - (detect_with_pull(GPIOD, 7, PULL_UP) << 3U); - - if (id2 == 3U) { - hw_type = HW_TYPE_CUATRO; - current_board = &board_cuatro; - } else if (id1 == 0U) { - hw_type = HW_TYPE_RED_PANDA; - current_board = &board_red; - } else if (id1 == 1U) { - // deprecated - //hw_type = HW_TYPE_RED_PANDA_V2; - hw_type = HW_TYPE_UNKNOWN; - } else if (id1 == 2U) { - hw_type = HW_TYPE_TRES; - current_board = &board_tres; - } else { - hw_type = HW_TYPE_UNKNOWN; - print("Hardware type is UNKNOWN!\n"); - } -} +void detect_board_type(void); diff --git a/board/stm32h7/clock.c b/board/stm32h7/clock.c new file mode 100644 index 00000000000..f305dff2fba --- /dev/null +++ b/board/stm32h7/clock.c @@ -0,0 +1,125 @@ +/* +HSE: 25MHz +PLL1Q: 80MHz (for FDCAN) +HSI48 enabled (for USB) +CPU: 240MHz +CPU Systick: 240MHz +AXI: 120MHz +HCLK3: 60MHz +APB3 per: 60MHz +AHB1,2 per: 120MHz +APB1 per: 60MHz +APB1 tim: 120MHz +APB2 per: 60MHz +APB2 tim: 120MHz +AHB4 per: 120MHz +APB4 per: 60MHz +PCLK1: 60MHz (for USART2,3,4,5,7,8) +*/ + +#include "board/config.h" +#include "board/stm32h7/clock.h" +#include "board/drivers/registers.h" + +typedef enum { + PACKAGE_UNKNOWN = 0, + PACKAGE_WITH_SMPS = 1, + PACKAGE_WITHOUT_SMPS = 2, +} PackageSMPSType; + +// TODO: find a better way to distinguish between H725 (using SMPS) and H723 (lacking SMPS) +// The package will do for now, since we have only used TFBGA100 for H723 +static PackageSMPSType get_package_smps_type(void) { + PackageSMPSType ret; + RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; // make sure SYSCFG clock is enabled. does seem to read fine without too though + + switch(SYSCFG->PKGR & 0xFU) { + case 0b0001U: // TFBGA100 Legacy + case 0b0011U: // TFBGA100 + ret = PACKAGE_WITHOUT_SMPS; + break; + case 0b0101U: // LQFP144 Legacy + case 0b0111U: // LQFP144 Industrial + case 0b1000U: // UFBGA169 + ret = PACKAGE_WITH_SMPS; + break; + default: + ret = PACKAGE_UNKNOWN; + break; + } + return ret; +} + +void clock_init(void) { + /* + WARNING: PWR->CR3's lower byte can only be written once + * subsequent writes will silently fail + * only cleared with a full power-on-reset, not soft reset or reset pin + * some H7 have a bootrom with a DFU routine that writes (and locks) CR3 + * if the CR3 config doesn't match the HW, the core will deadlock and require immediately going into DFU from a cold boot + + In a normal bootup, the bootstub will be the first to write this. The app section calls clock_init again, but the CR3 write will silently fail. This is fine for most cases, but caution should be taken that the bootstub and app always write the same config. + */ + + // Set power mode to direct SMPS power supply (depends on the board layout) + PackageSMPSType package_smps = get_package_smps_type(); + if (package_smps == PACKAGE_WITHOUT_SMPS) { + register_set(&(PWR->CR3), PWR_CR3_LDOEN, 0xFU); // no SMPS, so powered by LDO + } else if (package_smps == PACKAGE_WITH_SMPS) { + register_set(&(PWR->CR3), PWR_CR3_SMPSEN, 0xFU); // powered only by SMPS + } else { + while(true) {} // unknown package, let's hang here + } + + // Set VOS level (VOS3 to 170Mhz, VOS2 to 300Mhz, VOS1 to 400Mhz, VOS0 to 550Mhz) + register_set(&(PWR->D3CR), PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0, 0xC000U); //VOS1, needed for 80Mhz CAN FD + while ((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0U) {} + while ((PWR->CSR1 & PWR_CSR1_ACTVOS) != (PWR->D3CR & PWR_D3CR_VOS)) {} // check that VOS level was actually set + + // Configure Flash ACR register LATENCY and WRHIGHFREQ (VOS0 range!) + register_set(&(FLASH->ACR), FLASH_ACR_LATENCY_2WS | 0x20U, 0x3FU); // VOS2, AXI 100MHz-150MHz + // enable external oscillator HSE + register_set_bits(&(RCC->CR), RCC_CR_HSEON); + while ((RCC->CR & RCC_CR_HSERDY) == 0U) {} + // enable internal HSI48 for USB FS kernel + register_set_bits(&(RCC->CR), RCC_CR_HSI48ON); + while ((RCC->CR & RCC_CR_HSI48RDY) == 0U) {} + // Specify the frequency source for PLL1, divider for DIVM1, DIVM2, DIVM3 : HSE, 5, 5, 5 + register_set(&(RCC->PLLCKSELR), RCC_PLLCKSELR_PLLSRC_HSE | RCC_PLLCKSELR_DIVM1_0 | RCC_PLLCKSELR_DIVM1_2 | RCC_PLLCKSELR_DIVM2_0 | RCC_PLLCKSELR_DIVM2_2 | RCC_PLLCKSELR_DIVM3_0 | RCC_PLLCKSELR_DIVM3_2, 0x3F3F3F3U); + + // *** PLL1 start *** + // Specify multiplier N and dividers P, Q, R for PLL1 : 48, 1, 3, 2 (clock 240Mhz, PLL1Q 80Mhz for CAN FD) + register_set(&(RCC->PLL1DIVR), 0x102002FU, 0x7F7FFFFFU); + // Specify the input and output frequency ranges, enable dividers for PLL1 + register_set(&(RCC->PLLCFGR), RCC_PLLCFGR_PLL1RGE_2 | RCC_PLLCFGR_DIVP1EN | RCC_PLLCFGR_DIVQ1EN | RCC_PLLCFGR_DIVR1EN, 0x7000CU); + // Enable PLL1 + register_set_bits(&(RCC->CR), RCC_CR_PLL1ON); + while((RCC->CR & RCC_CR_PLL1RDY) == 0U) {} + // *** PLL1 end *** + + //////////////OTHER CLOCKS//////////////////// + // RCC HCLK Clock Source / RCC APB3 Clock Source / RCC SYS Clock Source + register_set(&(RCC->D1CFGR), RCC_D1CFGR_HPRE_DIV2 | RCC_D1CFGR_D1PPRE_DIV2 | RCC_D1CFGR_D1CPRE_DIV1, 0xF7FU); + // RCC APB1 Clock Source / RCC APB2 Clock Source + register_set(&(RCC->D2CFGR), RCC_D2CFGR_D2PPRE1_DIV2 | RCC_D2CFGR_D2PPRE2_DIV2, 0x770U); + // RCC APB4 Clock Source + register_set(&(RCC->D3CFGR), RCC_D3CFGR_D3PPRE_DIV2, 0x70U); + + // Set SysClock source to PLL + register_set(&(RCC->CFGR), RCC_CFGR_SW_PLL1, 0x7U); + while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL1) {} + //////////////END OTHER CLOCKS//////////////////// + + // Configure clock source for USB (HSI48) + register_set(&(RCC->D2CCIP2R), RCC_D2CCIP2R_USBSEL_1 | RCC_D2CCIP2R_USBSEL_0, RCC_D2CCIP2R_USBSEL); + // Configure clock source for FDCAN (PLL1Q at 80Mhz) + register_set(&(RCC->D2CCIP1R), RCC_D2CCIP1R_FDCANSEL_0, RCC_D2CCIP1R_FDCANSEL); + // Configure clock source for DFSDM1 + register_set_bits(&(RCC->D2CCIP1R), RCC_D2CCIP1R_DFSDM1SEL); + // Configure clock source for ADC1,2,3 (per_ck(currently HSE)) + register_set(&(RCC->D3CCIPR), RCC_D3CCIPR_ADCSEL_1, RCC_D3CCIPR_ADCSEL); + //Enable the Clock Security System + register_set_bits(&(RCC->CR), RCC_CR_CSSHSEON); + //Enable Vdd33usb supply level detector + register_set_bits(&(PWR->CR3), PWR_CR3_USB33DEN); +} diff --git a/board/stm32h7/clock.h b/board/stm32h7/clock.h index 40ad06043e5..92f26c303c7 100644 --- a/board/stm32h7/clock.h +++ b/board/stm32h7/clock.h @@ -1,120 +1,3 @@ -/* -HSE: 25MHz -PLL1Q: 80MHz (for FDCAN) -HSI48 enabled (for USB) -CPU: 240MHz -CPU Systick: 240MHz -AXI: 120MHz -HCLK3: 60MHz -APB3 per: 60MHz -AHB1,2 per: 120MHz -APB1 per: 60MHz -APB1 tim: 120MHz -APB2 per: 60MHz -APB2 tim: 120MHz -AHB4 per: 120MHz -APB4 per: 60MHz -PCLK1: 60MHz (for USART2,3,4,5,7,8) -*/ +#pragma once -typedef enum { - PACKAGE_UNKNOWN = 0, - PACKAGE_WITH_SMPS = 1, - PACKAGE_WITHOUT_SMPS = 2, -} PackageSMPSType; - -// TODO: find a better way to distinguish between H725 (using SMPS) and H723 (lacking SMPS) -// The package will do for now, since we have only used TFBGA100 for H723 -static PackageSMPSType get_package_smps_type(void) { - PackageSMPSType ret; - RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; // make sure SYSCFG clock is enabled. does seem to read fine without too though - - switch(SYSCFG->PKGR & 0xFU) { - case 0b0001U: // TFBGA100 Legacy - case 0b0011U: // TFBGA100 - ret = PACKAGE_WITHOUT_SMPS; - break; - case 0b0101U: // LQFP144 Legacy - case 0b0111U: // LQFP144 Industrial - case 0b1000U: // UFBGA169 - ret = PACKAGE_WITH_SMPS; - break; - default: - ret = PACKAGE_UNKNOWN; - } - return ret; -} - -void clock_init(void) { - /* - WARNING: PWR->CR3's lower byte can only be written once - * subsequent writes will silently fail - * only cleared with a full power-on-reset, not soft reset or reset pin - * some H7 have a bootrom with a DFU routine that writes (and locks) CR3 - * if the CR3 config doesn't match the HW, the core will deadlock and require immediately going into DFU from a cold boot - - In a normal bootup, the bootstub will be the first to write this. The app section calls clock_init again, but the CR3 write will silently fail. This is fine for most cases, but caution should be taken that the bootstub and app always write the same config. - */ - - // Set power mode to direct SMPS power supply (depends on the board layout) - PackageSMPSType package_smps = get_package_smps_type(); - if (package_smps == PACKAGE_WITHOUT_SMPS) { - register_set(&(PWR->CR3), PWR_CR3_LDOEN, 0xFU); // no SMPS, so powered by LDO - } else if (package_smps == PACKAGE_WITH_SMPS) { - register_set(&(PWR->CR3), PWR_CR3_SMPSEN, 0xFU); // powered only by SMPS - } else { - while(true); // unknown package, let's hang here - } - - // Set VOS level (VOS3 to 170Mhz, VOS2 to 300Mhz, VOS1 to 400Mhz, VOS0 to 550Mhz) - register_set(&(PWR->D3CR), PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0, 0xC000U); //VOS1, needed for 80Mhz CAN FD - while ((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0U); - while ((PWR->CSR1 & PWR_CSR1_ACTVOS) != (PWR->D3CR & PWR_D3CR_VOS)); // check that VOS level was actually set - - // Configure Flash ACR register LATENCY and WRHIGHFREQ (VOS0 range!) - register_set(&(FLASH->ACR), FLASH_ACR_LATENCY_2WS | 0x20U, 0x3FU); // VOS2, AXI 100MHz-150MHz - // enable external oscillator HSE - register_set_bits(&(RCC->CR), RCC_CR_HSEON); - while ((RCC->CR & RCC_CR_HSERDY) == 0U); - // enable internal HSI48 for USB FS kernel - register_set_bits(&(RCC->CR), RCC_CR_HSI48ON); - while ((RCC->CR & RCC_CR_HSI48RDY) == 0U); - // Specify the frequency source for PLL1, divider for DIVM1, DIVM2, DIVM3 : HSE, 5, 5, 5 - register_set(&(RCC->PLLCKSELR), RCC_PLLCKSELR_PLLSRC_HSE | RCC_PLLCKSELR_DIVM1_0 | RCC_PLLCKSELR_DIVM1_2 | RCC_PLLCKSELR_DIVM2_0 | RCC_PLLCKSELR_DIVM2_2 | RCC_PLLCKSELR_DIVM3_0 | RCC_PLLCKSELR_DIVM3_2, 0x3F3F3F3U); - - // *** PLL1 start *** - // Specify multiplier N and dividers P, Q, R for PLL1 : 48, 1, 3, 2 (clock 240Mhz, PLL1Q 80Mhz for CAN FD) - register_set(&(RCC->PLL1DIVR), 0x102002FU, 0x7F7FFFFFU); - // Specify the input and output frequency ranges, enable dividers for PLL1 - register_set(&(RCC->PLLCFGR), RCC_PLLCFGR_PLL1RGE_2 | RCC_PLLCFGR_DIVP1EN | RCC_PLLCFGR_DIVQ1EN | RCC_PLLCFGR_DIVR1EN, 0x7000CU); - // Enable PLL1 - register_set_bits(&(RCC->CR), RCC_CR_PLL1ON); - while((RCC->CR & RCC_CR_PLL1RDY) == 0U); - // *** PLL1 end *** - - //////////////OTHER CLOCKS//////////////////// - // RCC HCLK Clock Source / RCC APB3 Clock Source / RCC SYS Clock Source - register_set(&(RCC->D1CFGR), RCC_D1CFGR_HPRE_DIV2 | RCC_D1CFGR_D1PPRE_DIV2 | RCC_D1CFGR_D1CPRE_DIV1, 0xF7FU); - // RCC APB1 Clock Source / RCC APB2 Clock Source - register_set(&(RCC->D2CFGR), RCC_D2CFGR_D2PPRE1_DIV2 | RCC_D2CFGR_D2PPRE2_DIV2, 0x770U); - // RCC APB4 Clock Source - register_set(&(RCC->D3CFGR), RCC_D3CFGR_D3PPRE_DIV2, 0x70U); - - // Set SysClock source to PLL - register_set(&(RCC->CFGR), RCC_CFGR_SW_PLL1, 0x7U); - while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL1); - //////////////END OTHER CLOCKS//////////////////// - - // Configure clock source for USB (HSI48) - register_set(&(RCC->D2CCIP2R), RCC_D2CCIP2R_USBSEL_1 | RCC_D2CCIP2R_USBSEL_0, RCC_D2CCIP2R_USBSEL); - // Configure clock source for FDCAN (PLL1Q at 80Mhz) - register_set(&(RCC->D2CCIP1R), RCC_D2CCIP1R_FDCANSEL_0, RCC_D2CCIP1R_FDCANSEL); - // Configure clock source for DFSDM1 - register_set_bits(&(RCC->D2CCIP1R), RCC_D2CCIP1R_DFSDM1SEL); - // Configure clock source for ADC1,2,3 (per_ck(currently HSE)) - register_set(&(RCC->D3CCIPR), RCC_D3CCIPR_ADCSEL_1, RCC_D3CCIPR_ADCSEL); - //Enable the Clock Security System - register_set_bits(&(RCC->CR), RCC_CR_CSSHSEON); - //Enable Vdd33usb supply level detector - register_set_bits(&(PWR->CR3), PWR_CR3_USB33DEN); -} +void clock_init(void); diff --git a/board/stm32h7/interrupt_handlers.c b/board/stm32h7/interrupt_handlers.c new file mode 100644 index 00000000000..5b8346de4dd --- /dev/null +++ b/board/stm32h7/interrupt_handlers.c @@ -0,0 +1,147 @@ +#include "board/config.h" +#include "board/drivers/interrupts.h" +#include "board/stm32h7/interrupt_handlers.h" + +// ********************* Bare interrupt handlers ********************* +// Interrupts for STM32H7x5 + +void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);} +void PVD_AVD_IRQHandler(void) {handle_interrupt(PVD_AVD_IRQn);} +void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);} +void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);} +void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);} +void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);} +void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);} +void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);} +void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);} +void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);} +void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);} +void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);} +void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);} +void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);} +void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);} +void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);} +void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);} +void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);} +void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);} +void FDCAN1_IT0_IRQHandler(void) {handle_interrupt(FDCAN1_IT0_IRQn);} +void FDCAN2_IT0_IRQHandler(void) {handle_interrupt(FDCAN2_IT0_IRQn);} +void FDCAN1_IT1_IRQHandler(void) {handle_interrupt(FDCAN1_IT1_IRQn);} +void FDCAN2_IT1_IRQHandler(void) {handle_interrupt(FDCAN2_IT1_IRQn);} +void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);} +void TIM1_BRK_IRQHandler(void) {handle_interrupt(TIM1_BRK_IRQn);} +void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);} +void TIM1_TRG_COM_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_IRQn);} +void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);} +void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);} +void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);} +void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);} +void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);} +void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);} +void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);} +void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);} +void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);} +void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);} +void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);} +void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);} +void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);} +void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);} +void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);} +void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);} +void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);} +void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);} +void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);} +void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);} +void FMC_IRQHandler(void) {handle_interrupt(FMC_IRQn);} +void SDMMC1_IRQHandler(void) {handle_interrupt(SDMMC1_IRQn);} +void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);} +void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);} +void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);} +void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);} +void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);} +void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);} +void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);} +void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);} +void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);} +void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);} +void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);} +void ETH_IRQHandler(void) {handle_interrupt(ETH_IRQn);} +void ETH_WKUP_IRQHandler(void) {handle_interrupt(ETH_WKUP_IRQn);} +void FDCAN_CAL_IRQHandler(void) {handle_interrupt(FDCAN_CAL_IRQn);} +void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);} +void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);} +void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);} +void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);} +void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);} +void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);} +void OTG_HS_EP1_OUT_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_OUT_IRQn);} +void OTG_HS_EP1_IN_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_IN_IRQn);} +void OTG_HS_WKUP_IRQHandler(void) {handle_interrupt(OTG_HS_WKUP_IRQn);} +void OTG_HS_IRQHandler(void) {handle_interrupt(OTG_HS_IRQn);} +void DCMI_PSSI_IRQHandler(void) {handle_interrupt(DCMI_PSSI_IRQn);} +void CRYP_IRQHandler(void) {handle_interrupt(CRYP_IRQn);} +void HASH_RNG_IRQHandler(void) {handle_interrupt(HASH_RNG_IRQn);} +void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);} +void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);} +void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);} +void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);} +void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);} +void SPI6_IRQHandler(void) {handle_interrupt(SPI6_IRQn);} +void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);} +void LTDC_IRQHandler(void) {handle_interrupt(LTDC_IRQn);} +void LTDC_ER_IRQHandler(void) {handle_interrupt(LTDC_ER_IRQn);} +void DMA2D_IRQHandler(void) {handle_interrupt(DMA2D_IRQn);} +void OCTOSPI1_IRQHandler(void) {handle_interrupt(OCTOSPI1_IRQn);} +void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);} +void CEC_IRQHandler(void) {handle_interrupt(CEC_IRQn);} +void I2C4_EV_IRQHandler(void) {handle_interrupt(I2C4_EV_IRQn);} +void I2C4_ER_IRQHandler(void) {handle_interrupt(I2C4_ER_IRQn);} +void SPDIF_RX_IRQHandler(void) {handle_interrupt(SPDIF_RX_IRQn);} +void DMAMUX1_OVR_IRQHandler(void) {handle_interrupt(DMAMUX1_OVR_IRQn);} +void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);} +void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);} +void DFSDM1_FLT2_IRQHandler(void) {handle_interrupt(DFSDM1_FLT2_IRQn);} +void DFSDM1_FLT3_IRQHandler(void) {handle_interrupt(DFSDM1_FLT3_IRQn);} +void SWPMI1_IRQHandler(void) {handle_interrupt(SWPMI1_IRQn);} +void TIM15_IRQHandler(void) {handle_interrupt(TIM15_IRQn);} +void TIM16_IRQHandler(void) {handle_interrupt(TIM16_IRQn);} +void TIM17_IRQHandler(void) {handle_interrupt(TIM17_IRQn);} +void MDIOS_WKUP_IRQHandler(void) {handle_interrupt(MDIOS_WKUP_IRQn);} +void MDIOS_IRQHandler(void) {handle_interrupt(MDIOS_IRQn);} +void MDMA_IRQHandler(void) {handle_interrupt(MDMA_IRQn);} +void SDMMC2_IRQHandler(void) {handle_interrupt(SDMMC2_IRQn);} +void HSEM1_IRQHandler(void) {handle_interrupt(HSEM1_IRQn);} +void ADC3_IRQHandler(void) {handle_interrupt(ADC3_IRQn);} +void DMAMUX2_OVR_IRQHandler(void) {handle_interrupt(DMAMUX2_OVR_IRQn);} +void BDMA_Channel0_IRQHandler(void) {handle_interrupt(BDMA_Channel0_IRQn);} +void BDMA_Channel1_IRQHandler(void) {handle_interrupt(BDMA_Channel1_IRQn);} +void BDMA_Channel2_IRQHandler(void) {handle_interrupt(BDMA_Channel2_IRQn);} +void BDMA_Channel3_IRQHandler(void) {handle_interrupt(BDMA_Channel3_IRQn);} +void BDMA_Channel4_IRQHandler(void) {handle_interrupt(BDMA_Channel4_IRQn);} +void BDMA_Channel5_IRQHandler(void) {handle_interrupt(BDMA_Channel5_IRQn);} +void BDMA_Channel6_IRQHandler(void) {handle_interrupt(BDMA_Channel6_IRQn);} +void BDMA_Channel7_IRQHandler(void) {handle_interrupt(BDMA_Channel7_IRQn);} +void COMP_IRQHandler(void) {handle_interrupt(COMP_IRQn);} +void LPTIM2_IRQHandler(void) {handle_interrupt(LPTIM2_IRQn);} +void LPTIM3_IRQHandler(void) {handle_interrupt(LPTIM3_IRQn);} +void LPTIM4_IRQHandler(void) {handle_interrupt(LPTIM4_IRQn);} +void LPTIM5_IRQHandler(void) {handle_interrupt(LPTIM5_IRQn);} +void LPUART1_IRQHandler(void) {handle_interrupt(LPUART1_IRQn);} +void CRS_IRQHandler(void) {handle_interrupt(CRS_IRQn);} +void ECC_IRQHandler(void) {handle_interrupt(ECC_IRQn);} +void SAI4_IRQHandler(void) {handle_interrupt(SAI4_IRQn);} +void DTS_IRQHandler(void) {handle_interrupt(DTS_IRQn);} +void WAKEUP_PIN_IRQHandler(void) {handle_interrupt(WAKEUP_PIN_IRQn);} +void OCTOSPI2_IRQHandler(void) {handle_interrupt(OCTOSPI2_IRQn);} +void OTFDEC1_IRQHandler(void) {handle_interrupt(OTFDEC1_IRQn);} +void OTFDEC2_IRQHandler(void) {handle_interrupt(OTFDEC2_IRQn);} +void FMAC_IRQHandler(void) {handle_interrupt(FMAC_IRQn);} +void CORDIC_IRQHandler(void) {handle_interrupt(CORDIC_IRQn);} +void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);} +void USART10_IRQHandler(void) {handle_interrupt(USART10_IRQn);} +void I2C5_EV_IRQHandler(void) {handle_interrupt(I2C5_EV_IRQn);} +void I2C5_ER_IRQHandler(void) {handle_interrupt(I2C5_ER_IRQn);} +void FDCAN3_IT0_IRQHandler(void) {handle_interrupt(FDCAN3_IT0_IRQn);} +void FDCAN3_IT1_IRQHandler(void) {handle_interrupt(FDCAN3_IT1_IRQn);} +void TIM23_IRQHandler(void) {handle_interrupt(TIM23_IRQn);} +void TIM24_IRQHandler(void) {handle_interrupt(TIM24_IRQn);} diff --git a/board/stm32h7/interrupt_handlers.h b/board/stm32h7/interrupt_handlers.h index 8148021d697..f496e751755 100644 --- a/board/stm32h7/interrupt_handlers.h +++ b/board/stm32h7/interrupt_handlers.h @@ -1,143 +1,142 @@ -// ********************* Bare interrupt handlers ********************* -// Interrupts for STM32H7x5 +#pragma once -void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);} -void PVD_AVD_IRQHandler(void) {handle_interrupt(PVD_AVD_IRQn);} -void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);} -void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);} -void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);} -void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);} -void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);} -void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);} -void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);} -void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);} -void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);} -void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);} -void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);} -void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);} -void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);} -void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);} -void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);} -void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);} -void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);} -void FDCAN1_IT0_IRQHandler(void) {handle_interrupt(FDCAN1_IT0_IRQn);} -void FDCAN2_IT0_IRQHandler(void) {handle_interrupt(FDCAN2_IT0_IRQn);} -void FDCAN1_IT1_IRQHandler(void) {handle_interrupt(FDCAN1_IT1_IRQn);} -void FDCAN2_IT1_IRQHandler(void) {handle_interrupt(FDCAN2_IT1_IRQn);} -void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);} -void TIM1_BRK_IRQHandler(void) {handle_interrupt(TIM1_BRK_IRQn);} -void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);} -void TIM1_TRG_COM_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_IRQn);} -void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);} -void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);} -void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);} -void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);} -void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);} -void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);} -void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);} -void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);} -void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);} -void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);} -void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);} -void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);} -void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);} -void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);} -void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);} -void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);} -void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);} -void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);} -void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);} -void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);} -void FMC_IRQHandler(void) {handle_interrupt(FMC_IRQn);} -void SDMMC1_IRQHandler(void) {handle_interrupt(SDMMC1_IRQn);} -void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);} -void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);} -void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);} -void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);} -void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);} -void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);} -void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);} -void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);} -void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);} -void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);} -void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);} -void ETH_IRQHandler(void) {handle_interrupt(ETH_IRQn);} -void ETH_WKUP_IRQHandler(void) {handle_interrupt(ETH_WKUP_IRQn);} -void FDCAN_CAL_IRQHandler(void) {handle_interrupt(FDCAN_CAL_IRQn);} -void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);} -void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);} -void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);} -void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);} -void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);} -void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);} -void OTG_HS_EP1_OUT_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_OUT_IRQn);} -void OTG_HS_EP1_IN_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_IN_IRQn);} -void OTG_HS_WKUP_IRQHandler(void) {handle_interrupt(OTG_HS_WKUP_IRQn);} -void OTG_HS_IRQHandler(void) {handle_interrupt(OTG_HS_IRQn);} -void DCMI_PSSI_IRQHandler(void) {handle_interrupt(DCMI_PSSI_IRQn);} -void CRYP_IRQHandler(void) {handle_interrupt(CRYP_IRQn);} -void HASH_RNG_IRQHandler(void) {handle_interrupt(HASH_RNG_IRQn);} -void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);} -void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);} -void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);} -void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);} -void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);} -void SPI6_IRQHandler(void) {handle_interrupt(SPI6_IRQn);} -void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);} -void LTDC_IRQHandler(void) {handle_interrupt(LTDC_IRQn);} -void LTDC_ER_IRQHandler(void) {handle_interrupt(LTDC_ER_IRQn);} -void DMA2D_IRQHandler(void) {handle_interrupt(DMA2D_IRQn);} -void OCTOSPI1_IRQHandler(void) {handle_interrupt(OCTOSPI1_IRQn);} -void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);} -void CEC_IRQHandler(void) {handle_interrupt(CEC_IRQn);} -void I2C4_EV_IRQHandler(void) {handle_interrupt(I2C4_EV_IRQn);} -void I2C4_ER_IRQHandler(void) {handle_interrupt(I2C4_ER_IRQn);} -void SPDIF_RX_IRQHandler(void) {handle_interrupt(SPDIF_RX_IRQn);} -void DMAMUX1_OVR_IRQHandler(void) {handle_interrupt(DMAMUX1_OVR_IRQn);} -void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);} -void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);} -void DFSDM1_FLT2_IRQHandler(void) {handle_interrupt(DFSDM1_FLT2_IRQn);} -void DFSDM1_FLT3_IRQHandler(void) {handle_interrupt(DFSDM1_FLT3_IRQn);} -void SWPMI1_IRQHandler(void) {handle_interrupt(SWPMI1_IRQn);} -void TIM15_IRQHandler(void) {handle_interrupt(TIM15_IRQn);} -void TIM16_IRQHandler(void) {handle_interrupt(TIM16_IRQn);} -void TIM17_IRQHandler(void) {handle_interrupt(TIM17_IRQn);} -void MDIOS_WKUP_IRQHandler(void) {handle_interrupt(MDIOS_WKUP_IRQn);} -void MDIOS_IRQHandler(void) {handle_interrupt(MDIOS_IRQn);} -void MDMA_IRQHandler(void) {handle_interrupt(MDMA_IRQn);} -void SDMMC2_IRQHandler(void) {handle_interrupt(SDMMC2_IRQn);} -void HSEM1_IRQHandler(void) {handle_interrupt(HSEM1_IRQn);} -void ADC3_IRQHandler(void) {handle_interrupt(ADC3_IRQn);} -void DMAMUX2_OVR_IRQHandler(void) {handle_interrupt(DMAMUX2_OVR_IRQn);} -void BDMA_Channel0_IRQHandler(void) {handle_interrupt(BDMA_Channel0_IRQn);} -void BDMA_Channel1_IRQHandler(void) {handle_interrupt(BDMA_Channel1_IRQn);} -void BDMA_Channel2_IRQHandler(void) {handle_interrupt(BDMA_Channel2_IRQn);} -void BDMA_Channel3_IRQHandler(void) {handle_interrupt(BDMA_Channel3_IRQn);} -void BDMA_Channel4_IRQHandler(void) {handle_interrupt(BDMA_Channel4_IRQn);} -void BDMA_Channel5_IRQHandler(void) {handle_interrupt(BDMA_Channel5_IRQn);} -void BDMA_Channel6_IRQHandler(void) {handle_interrupt(BDMA_Channel6_IRQn);} -void BDMA_Channel7_IRQHandler(void) {handle_interrupt(BDMA_Channel7_IRQn);} -void COMP_IRQHandler(void) {handle_interrupt(COMP_IRQn);} -void LPTIM2_IRQHandler(void) {handle_interrupt(LPTIM2_IRQn);} -void LPTIM3_IRQHandler(void) {handle_interrupt(LPTIM3_IRQn);} -void LPTIM4_IRQHandler(void) {handle_interrupt(LPTIM4_IRQn);} -void LPTIM5_IRQHandler(void) {handle_interrupt(LPTIM5_IRQn);} -void LPUART1_IRQHandler(void) {handle_interrupt(LPUART1_IRQn);} -void CRS_IRQHandler(void) {handle_interrupt(CRS_IRQn);} -void ECC_IRQHandler(void) {handle_interrupt(ECC_IRQn);} -void SAI4_IRQHandler(void) {handle_interrupt(SAI4_IRQn);} -void DTS_IRQHandler(void) {handle_interrupt(DTS_IRQn);} -void WAKEUP_PIN_IRQHandler(void) {handle_interrupt(WAKEUP_PIN_IRQn);} -void OCTOSPI2_IRQHandler(void) {handle_interrupt(OCTOSPI2_IRQn);} -void OTFDEC1_IRQHandler(void) {handle_interrupt(OTFDEC1_IRQn);} -void OTFDEC2_IRQHandler(void) {handle_interrupt(OTFDEC2_IRQn);} -void FMAC_IRQHandler(void) {handle_interrupt(FMAC_IRQn);} -void CORDIC_IRQHandler(void) {handle_interrupt(CORDIC_IRQn);} -void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);} -void USART10_IRQHandler(void) {handle_interrupt(USART10_IRQn);} -void I2C5_EV_IRQHandler(void) {handle_interrupt(I2C5_EV_IRQn);} -void I2C5_ER_IRQHandler(void) {handle_interrupt(I2C5_ER_IRQn);} -void FDCAN3_IT0_IRQHandler(void) {handle_interrupt(FDCAN3_IT0_IRQn);} -void FDCAN3_IT1_IRQHandler(void) {handle_interrupt(FDCAN3_IT1_IRQn);} -void TIM23_IRQHandler(void) {handle_interrupt(TIM23_IRQn);} -void TIM24_IRQHandler(void) {handle_interrupt(TIM24_IRQn);} +void WWDG_IRQHandler(void); +void PVD_AVD_IRQHandler(void); +void TAMP_STAMP_IRQHandler(void); +void RTC_WKUP_IRQHandler(void); +void FLASH_IRQHandler(void); +void RCC_IRQHandler(void); +void EXTI0_IRQHandler(void); +void EXTI1_IRQHandler(void); +void EXTI2_IRQHandler(void); +void EXTI3_IRQHandler(void); +void EXTI4_IRQHandler(void); +void DMA1_Stream0_IRQHandler(void); +void DMA1_Stream1_IRQHandler(void); +void DMA1_Stream2_IRQHandler(void); +void DMA1_Stream3_IRQHandler(void); +void DMA1_Stream4_IRQHandler(void); +void DMA1_Stream5_IRQHandler(void); +void DMA1_Stream6_IRQHandler(void); +void ADC_IRQHandler(void); +void FDCAN1_IT0_IRQHandler(void); +void FDCAN2_IT0_IRQHandler(void); +void FDCAN1_IT1_IRQHandler(void); +void FDCAN2_IT1_IRQHandler(void); +void EXTI9_5_IRQHandler(void); +void TIM1_BRK_IRQHandler(void); +void TIM1_UP_TIM10_IRQHandler(void); +void TIM1_TRG_COM_IRQHandler(void); +void TIM1_CC_IRQHandler(void); +void TIM2_IRQHandler(void); +void TIM3_IRQHandler(void); +void TIM4_IRQHandler(void); +void I2C1_EV_IRQHandler(void); +void I2C1_ER_IRQHandler(void); +void I2C2_EV_IRQHandler(void); +void I2C2_ER_IRQHandler(void); +void SPI1_IRQHandler(void); +void SPI2_IRQHandler(void); +void USART1_IRQHandler(void); +void USART2_IRQHandler(void); +void USART3_IRQHandler(void); +void EXTI15_10_IRQHandler(void); +void RTC_Alarm_IRQHandler(void); +void TIM8_BRK_TIM12_IRQHandler(void); +void TIM8_UP_TIM13_IRQHandler(void); +void TIM8_TRG_COM_TIM14_IRQHandler(void); +void TIM8_CC_IRQHandler(void); +void DMA1_Stream7_IRQHandler(void); +void FMC_IRQHandler(void); +void SDMMC1_IRQHandler(void); +void TIM5_IRQHandler(void); +void SPI3_IRQHandler(void); +void UART4_IRQHandler(void); +void UART5_IRQHandler(void); +void TIM6_DAC_IRQHandler(void); +void TIM7_IRQHandler(void); +void DMA2_Stream0_IRQHandler(void); +void DMA2_Stream1_IRQHandler(void); +void DMA2_Stream2_IRQHandler(void); +void DMA2_Stream3_IRQHandler(void); +void DMA2_Stream4_IRQHandler(void); +void ETH_IRQHandler(void); +void ETH_WKUP_IRQHandler(void); +void FDCAN_CAL_IRQHandler(void); +void DMA2_Stream5_IRQHandler(void); +void DMA2_Stream6_IRQHandler(void); +void DMA2_Stream7_IRQHandler(void); +void USART6_IRQHandler(void); +void I2C3_EV_IRQHandler(void); +void I2C3_ER_IRQHandler(void); +void OTG_HS_EP1_OUT_IRQHandler(void); +void OTG_HS_EP1_IN_IRQHandler(void); +void OTG_HS_WKUP_IRQHandler(void); +void OTG_HS_IRQHandler(void); +void DCMI_PSSI_IRQHandler(void); +void CRYP_IRQHandler(void); +void HASH_RNG_IRQHandler(void); +void FPU_IRQHandler(void); +void UART7_IRQHandler(void); +void UART8_IRQHandler(void); +void SPI4_IRQHandler(void); +void SPI5_IRQHandler(void); +void SPI6_IRQHandler(void); +void SAI1_IRQHandler(void); +void LTDC_IRQHandler(void); +void LTDC_ER_IRQHandler(void); +void DMA2D_IRQHandler(void); +void OCTOSPI1_IRQHandler(void); +void LPTIM1_IRQHandler(void); +void CEC_IRQHandler(void); +void I2C4_EV_IRQHandler(void); +void I2C4_ER_IRQHandler(void); +void SPDIF_RX_IRQHandler(void); +void DMAMUX1_OVR_IRQHandler(void); +void DFSDM1_FLT0_IRQHandler(void); +void DFSDM1_FLT1_IRQHandler(void); +void DFSDM1_FLT2_IRQHandler(void); +void DFSDM1_FLT3_IRQHandler(void); +void SWPMI1_IRQHandler(void); +void TIM15_IRQHandler(void); +void TIM16_IRQHandler(void); +void TIM17_IRQHandler(void); +void MDIOS_WKUP_IRQHandler(void); +void MDIOS_IRQHandler(void); +void MDMA_IRQHandler(void); +void SDMMC2_IRQHandler(void); +void HSEM1_IRQHandler(void); +void ADC3_IRQHandler(void); +void DMAMUX2_OVR_IRQHandler(void); +void BDMA_Channel0_IRQHandler(void); +void BDMA_Channel1_IRQHandler(void); +void BDMA_Channel2_IRQHandler(void); +void BDMA_Channel3_IRQHandler(void); +void BDMA_Channel4_IRQHandler(void); +void BDMA_Channel5_IRQHandler(void); +void BDMA_Channel6_IRQHandler(void); +void BDMA_Channel7_IRQHandler(void); +void COMP_IRQHandler(void); +void LPTIM2_IRQHandler(void); +void LPTIM3_IRQHandler(void); +void LPTIM4_IRQHandler(void); +void LPTIM5_IRQHandler(void); +void LPUART1_IRQHandler(void); +void CRS_IRQHandler(void); +void ECC_IRQHandler(void); +void SAI4_IRQHandler(void); +void DTS_IRQHandler(void); +void WAKEUP_PIN_IRQHandler(void); +void OCTOSPI2_IRQHandler(void); +void OTFDEC1_IRQHandler(void); +void OTFDEC2_IRQHandler(void); +void FMAC_IRQHandler(void); +void CORDIC_IRQHandler(void); +void UART9_IRQHandler(void); +void USART10_IRQHandler(void); +void I2C5_EV_IRQHandler(void); +void I2C5_ER_IRQHandler(void); +void FDCAN3_IT0_IRQHandler(void); +void FDCAN3_IT1_IRQHandler(void); +void TIM23_IRQHandler(void); +void TIM24_IRQHandler(void); diff --git a/board/stm32h7/lladc.c b/board/stm32h7/lladc.c new file mode 100644 index 00000000000..142dcc0764c --- /dev/null +++ b/board/stm32h7/lladc.c @@ -0,0 +1,81 @@ +#include "board/config.h" +#include "board/stm32h7/lladc.h" + +static uint32_t adc_avdd_mV = 0U; + +void adc_init(ADC_TypeDef *adc) { + adc->CR &= ~(ADC_CR_ADEN); // Disable ADC + adc->CR &= ~(ADC_CR_DEEPPWD); // Reset deep-power-down mode + adc->CR |= ADC_CR_ADVREGEN; // Enable ADC regulator + while(!(adc->ISR & ADC_ISR_LDORDY) && (adc != ADC3)) {} + + if (adc != ADC3) { + adc->CR &= ~(ADC_CR_ADCALDIF); // Choose single-ended calibration + adc->CR |= ADC_CR_ADCALLIN; // Lineriality calibration + } + adc->CR |= ADC_CR_ADCAL; // Start calibration + while((adc->CR & ADC_CR_ADCAL) != 0U) {} + + adc->ISR |= ADC_ISR_ADRDY; + adc->CR |= ADC_CR_ADEN; + while(!(adc->ISR & ADC_ISR_ADRDY)) {} +} + +uint16_t adc_get_raw(const adc_signal_t *signal) { + signal->adc->SQR1 &= ~(ADC_SQR1_L); + signal->adc->SQR1 = ((uint32_t) signal->channel << 6U); + + // sample time + if (signal->channel < 10U) { + signal->adc->SMPR1 = ((uint32_t) signal->sample_time << (signal->channel * 3U)); + } else { + signal->adc->SMPR2 = ((uint32_t) signal->sample_time << ((signal->channel - 10U) * 3U)); + } + + // select channel + signal->adc->PCSEL_RES0 = (0x1UL << signal->channel); + + // oversampling + signal->adc->CFGR2 = (((1U << (uint32_t) signal->oversampling) - 1U) << ADC_CFGR2_OVSR_Pos) | ((uint32_t) signal->oversampling << ADC_CFGR2_OVSS_Pos); + signal->adc->CFGR2 |= (signal->oversampling != OVERSAMPLING_1) ? ADC_CFGR2_ROVSE : 0U; + + // start conversion + signal->adc->CR |= ADC_CR_ADSTART; + while (!(signal->adc->ISR & ADC_ISR_EOC)) {} + + uint16_t res = signal->adc->DR; + + while (!(signal->adc->ISR & ADC_ISR_EOS)) {} + signal->adc->ISR |= ADC_ISR_EOS; + + return res; +} + +static void adc_calibrate_vdda(void) { + // ADC2 used for calibration + adc_init(ADC2); + + // enable VREFINT channel + ADC3_COMMON->CCR |= ADC_CCR_VREFEN; + SYSCFG->ADC2ALT |= SYSCFG_ADC2ALT_ADC2_ROUT1; + + // measure VREFINT and derive AVDD + uint16_t raw_vrefint = adc_get_raw(&(adc_signal_t){.adc = ADC2, .channel = 17U, .sample_time = SAMPLETIME_810_CYCLES, .oversampling = OVERSAMPLING_256}); + adc_avdd_mV = (uint32_t) *VREFINT_CAL_ADDR * 16U * 3300U / raw_vrefint; + print(" AVDD: 0x"); puth(adc_avdd_mV); print(" mV\n"); +} + +uint16_t adc_get_mV(const adc_signal_t *signal) { + uint16_t ret = 0; + + if (adc_avdd_mV == 0U) { + adc_calibrate_vdda(); + } + + if ((signal->adc == ADC1) || (signal->adc == ADC2)) { + ret = (adc_get_raw(signal) * adc_avdd_mV) / 65535U; + } else if (signal->adc == ADC3) { + ret = (adc_get_raw(signal) * adc_avdd_mV) / 4095U; + } else {} + return ret; +} diff --git a/board/stm32h7/lladc.h b/board/stm32h7/lladc.h index 0a7e983c9ef..af5989b9691 100644 --- a/board/stm32h7/lladc.h +++ b/board/stm32h7/lladc.h @@ -1,80 +1,41 @@ -#include "lladc_declarations.h" - -static uint32_t adc_avdd_mV = 0U; - -void adc_init(ADC_TypeDef *adc) { - adc->CR &= ~(ADC_CR_ADEN); // Disable ADC - adc->CR &= ~(ADC_CR_DEEPPWD); // Reset deep-power-down mode - adc->CR |= ADC_CR_ADVREGEN; // Enable ADC regulator - while(!(adc->ISR & ADC_ISR_LDORDY) && (adc != ADC3)); - - if (adc != ADC3) { - adc->CR &= ~(ADC_CR_ADCALDIF); // Choose single-ended calibration - adc->CR |= ADC_CR_ADCALLIN; // Lineriality calibration - } - adc->CR |= ADC_CR_ADCAL; // Start calibration - while((adc->CR & ADC_CR_ADCAL) != 0U); - - adc->ISR |= ADC_ISR_ADRDY; - adc->CR |= ADC_CR_ADEN; - while(!(adc->ISR & ADC_ISR_ADRDY)); -} - -uint16_t adc_get_raw(const adc_signal_t *signal) { - signal->adc->SQR1 &= ~(ADC_SQR1_L); - signal->adc->SQR1 = ((uint32_t) signal->channel << 6U); - - // sample time - if (signal->channel < 10U) { - signal->adc->SMPR1 = ((uint32_t) signal->sample_time << (signal->channel * 3U)); - } else { - signal->adc->SMPR2 = ((uint32_t) signal->sample_time << ((signal->channel - 10U) * 3U)); - } - - // select channel - signal->adc->PCSEL_RES0 = (0x1UL << signal->channel); - - // oversampling - signal->adc->CFGR2 = (((1U << (uint32_t) signal->oversampling) - 1U) << ADC_CFGR2_OVSR_Pos) | ((uint32_t) signal->oversampling << ADC_CFGR2_OVSS_Pos); - signal->adc->CFGR2 |= (signal->oversampling != OVERSAMPLING_1) ? ADC_CFGR2_ROVSE : 0U; - - // start conversion - signal->adc->CR |= ADC_CR_ADSTART; - while (!(signal->adc->ISR & ADC_ISR_EOC)); - - uint16_t res = signal->adc->DR; - - while (!(signal->adc->ISR & ADC_ISR_EOS)); - signal->adc->ISR |= ADC_ISR_EOS; - - return res; -} - -static void adc_calibrate_vdda(void) { - // ADC2 used for calibration - adc_init(ADC2); - - // enable VREFINT channel - ADC3_COMMON->CCR |= ADC_CCR_VREFEN; - SYSCFG->ADC2ALT |= SYSCFG_ADC2ALT_ADC2_ROUT1; - - // measure VREFINT and derive AVDD - uint16_t raw_vrefint = adc_get_raw(&(adc_signal_t){.adc = ADC2, .channel = 17U, .sample_time = SAMPLETIME_810_CYCLES, .oversampling = OVERSAMPLING_256}); - adc_avdd_mV = (uint32_t) *VREFINT_CAL_ADDR * 16U * 3300U / raw_vrefint; - print(" AVDD: 0x"); puth(adc_avdd_mV); print(" mV\n"); -} - -uint16_t adc_get_mV(const adc_signal_t *signal) { - uint16_t ret = 0; - - if (adc_avdd_mV == 0U) { - adc_calibrate_vdda(); - } - - if ((signal->adc == ADC1) || (signal->adc == ADC2)) { - ret = (adc_get_raw(signal) * adc_avdd_mV) / 65535U; - } else if (signal->adc == ADC3) { - ret = (adc_get_raw(signal) * adc_avdd_mV) / 4095U; - } else {} - return ret; -} +#pragma once + +typedef enum { + SAMPLETIME_1_CYCLE = 0, + SAMPLETIME_2_CYCLES = 1, + SAMPLETIME_8_CYCLES = 2, + SAMPLETIME_16_CYCLES = 3, + SAMPLETIME_32_CYCLES = 4, + SAMPLETIME_64_CYCLES = 5, + SAMPLETIME_387_CYCLES = 6, + SAMPLETIME_810_CYCLES = 7 +} adc_sample_time_t; + +typedef enum { + OVERSAMPLING_1 = 0, + OVERSAMPLING_2 = 1, + OVERSAMPLING_4 = 2, + OVERSAMPLING_8 = 3, + OVERSAMPLING_16 = 4, + OVERSAMPLING_32 = 5, + OVERSAMPLING_64 = 6, + OVERSAMPLING_128 = 7, + OVERSAMPLING_256 = 8, + OVERSAMPLING_512 = 9, + OVERSAMPLING_1024 = 10 +} adc_oversampling_t; + +typedef struct { + ADC_TypeDef *adc; + uint8_t channel; + adc_sample_time_t sample_time; + adc_oversampling_t oversampling; +} adc_signal_t; + +#define ADC_CHANNEL_DEFAULT(a, c) {.adc = (a), .channel = (c), .sample_time = SAMPLETIME_32_CYCLES, .oversampling = OVERSAMPLING_64} + +#define VREFINT_CAL_ADDR ((uint16_t *)0x1FF1E860UL) + +void adc_init(ADC_TypeDef *adc); +uint16_t adc_get_raw(const adc_signal_t *signal); +uint16_t adc_get_mV(const adc_signal_t *signal); diff --git a/board/stm32h7/lladc_declarations.h b/board/stm32h7/lladc_declarations.h deleted file mode 100644 index 979919c8c98..00000000000 --- a/board/stm32h7/lladc_declarations.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -typedef enum { - SAMPLETIME_1_CYCLE = 0, - SAMPLETIME_2_CYCLES = 1, - SAMPLETIME_8_CYCLES = 2, - SAMPLETIME_16_CYCLES = 3, - SAMPLETIME_32_CYCLES = 4, - SAMPLETIME_64_CYCLES = 5, - SAMPLETIME_387_CYCLES = 6, - SAMPLETIME_810_CYCLES = 7 -} adc_sample_time_t; - -typedef enum { - OVERSAMPLING_1 = 0, - OVERSAMPLING_2 = 1, - OVERSAMPLING_4 = 2, - OVERSAMPLING_8 = 3, - OVERSAMPLING_16 = 4, - OVERSAMPLING_32 = 5, - OVERSAMPLING_64 = 6, - OVERSAMPLING_128 = 7, - OVERSAMPLING_256 = 8, - OVERSAMPLING_512 = 9, - OVERSAMPLING_1024 = 10 -} adc_oversampling_t; - -typedef struct { - ADC_TypeDef *adc; - uint8_t channel; - adc_sample_time_t sample_time; - adc_oversampling_t oversampling; -} adc_signal_t; - -#define ADC_CHANNEL_DEFAULT(a, c) {.adc = (a), .channel = (c), .sample_time = SAMPLETIME_32_CYCLES, .oversampling = OVERSAMPLING_64} - -#define VREFINT_CAL_ADDR ((uint16_t *)0x1FF1E860UL) diff --git a/board/stm32h7/llfan.c b/board/stm32h7/llfan.c new file mode 100644 index 00000000000..098a01fb1e2 --- /dev/null +++ b/board/stm32h7/llfan.c @@ -0,0 +1,26 @@ +#include "board/config.h" +#include "board/stm32h7/llfan.h" + +// TACH interrupt handler +static void EXTI2_IRQ_Handler(void) { + volatile unsigned int pr = EXTI->PR1 & (1U << 2); + if ((pr & (1U << 2)) != 0U) { + fan_state.tach_counter++; + } + EXTI->PR1 = (1U << 2); +} + +void llfan_init(void) { + // 12000RPM * 4 tach edges / 60 seconds + REGISTER_INTERRUPT(EXTI2_IRQn, EXTI2_IRQ_Handler, 1000U, FAULT_INTERRUPT_RATE_TACH) + + // Init PWM speed control + pwm_init(TIM3, 3); + + // Init TACH interrupt + register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI2_PD, 0xF00U); + register_set_bits(&(EXTI->IMR1), (1U << 2)); + register_set_bits(&(EXTI->RTSR1), (1U << 2)); + register_set_bits(&(EXTI->FTSR1), (1U << 2)); + NVIC_EnableIRQ(EXTI2_IRQn); +} diff --git a/board/stm32h7/llfan.h b/board/stm32h7/llfan.h index 06309d0633f..ef41b6789dd 100644 --- a/board/stm32h7/llfan.h +++ b/board/stm32h7/llfan.h @@ -1,23 +1,3 @@ -// TACH interrupt handler -static void EXTI2_IRQ_Handler(void) { - volatile unsigned int pr = EXTI->PR1 & (1U << 2); - if ((pr & (1U << 2)) != 0U) { - fan_state.tach_counter++; - } - EXTI->PR1 = (1U << 2); -} +#pragma once -void llfan_init(void) { - // 12000RPM * 4 tach edges / 60 seconds - REGISTER_INTERRUPT(EXTI2_IRQn, EXTI2_IRQ_Handler, 1000U, FAULT_INTERRUPT_RATE_TACH) - - // Init PWM speed control - pwm_init(TIM3, 3); - - // Init TACH interrupt - register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI2_PD, 0xF00U); - register_set_bits(&(EXTI->IMR1), (1U << 2)); - register_set_bits(&(EXTI->RTSR1), (1U << 2)); - register_set_bits(&(EXTI->FTSR1), (1U << 2)); - NVIC_EnableIRQ(EXTI2_IRQn); -} +void llfan_init(void); diff --git a/board/stm32h7/llfdcan.c b/board/stm32h7/llfdcan.c new file mode 100644 index 00000000000..2e97fece310 --- /dev/null +++ b/board/stm32h7/llfdcan.c @@ -0,0 +1,229 @@ +#include "board/config.h" +#include "board/stm32h7/llfdcan.h" + +// kbps multiplied by 10 +const uint32_t speeds[SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U}; +const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U, 20000U, 50000U}; + +static bool fdcan_request_init(FDCAN_GlobalTypeDef *FDCANx) { + bool ret = true; + // Exit from sleep mode + FDCANx->CCCR &= ~(FDCAN_CCCR_CSR); + while ((FDCANx->CCCR & FDCAN_CCCR_CSA) == FDCAN_CCCR_CSA) {} + + // Request init + uint32_t timeout_counter = 0U; + FDCANx->CCCR |= FDCAN_CCCR_INIT; + while ((FDCANx->CCCR & FDCAN_CCCR_INIT) == 0U) { + // Delay for about 1ms + delay(10000); + timeout_counter++; + + if (timeout_counter >= CAN_INIT_TIMEOUT_MS){ + ret = false; + break; + } + } + return ret; +} + +static bool fdcan_exit_init(FDCAN_GlobalTypeDef *FDCANx) { + bool ret = true; + + FDCANx->CCCR &= ~(FDCAN_CCCR_INIT); + uint32_t timeout_counter = 0U; + while ((FDCANx->CCCR & FDCAN_CCCR_INIT) != 0U) { + // Delay for about 1ms + delay(10000); + timeout_counter++; + + if (timeout_counter >= CAN_INIT_TIMEOUT_MS) { + ret = false; + break; + } + } + return ret; +} + +bool llcan_set_speed(FDCAN_GlobalTypeDef *FDCANx, uint32_t speed, uint32_t data_speed, bool non_iso, bool loopback, bool silent) { + UNUSED(speed); + bool ret = fdcan_request_init(FDCANx); + + if (ret) { + // Enable config change + FDCANx->CCCR |= FDCAN_CCCR_CCE; + + // Reset operation mode to Normal + //Reset operation mode to Normal + FDCANx->CCCR &= ~(FDCAN_CCCR_TEST); + FDCANx->TEST &= ~(FDCAN_TEST_LBCK); + FDCANx->CCCR &= ~(FDCAN_CCCR_MON); + FDCANx->CCCR &= ~(FDCAN_CCCR_ASM); + FDCANx->CCCR &= ~(FDCAN_CCCR_NISO); + + // TODO: add as a separate safety mode + // Enable ASM restricted operation(for debug or automatic bitrate switching) + //FDCANx->CCCR |= FDCAN_CCCR_ASM; + uint8_t prescaler = BITRATE_PRESCALER; + if (speed < 2500U) { + // The only way to support speeds lower than 250Kbit/s (down to 10Kbit/s) + prescaler = BITRATE_PRESCALER * 16U; + } + + // Set the nominal bit timing values + uint32_t tq = CAN_QUANTA(speed, prescaler); + uint32_t sp = CAN_SP_NOMINAL; + uint32_t seg1 = CAN_SEG1(tq, sp); + uint32_t seg2 = CAN_SEG2(tq, sp); + uint8_t sjw = MIN(127U, seg2); + + // Set the nominal bit timing values + FDCANx->NBTP = (((sjw & 0x7FUL)-1U)<DBTP = (((sjw & 0xFUL)-1U)<CCCR |= FDCAN_CCCR_NISO; + } + + // Silent loopback is known as internal loopback in the docs + if (loopback) { + FDCANx->CCCR |= FDCAN_CCCR_TEST; + FDCANx->TEST |= FDCAN_TEST_LBCK; + FDCANx->CCCR |= FDCAN_CCCR_MON; + } + // Silent is known as bus monitoring in the docs + if (silent) { + FDCANx->CCCR |= FDCAN_CCCR_MON; + } + ret = fdcan_exit_init(FDCANx); + if (!ret) { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (2)\n"); + } + } else { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (1)\n"); + } + return ret; +} + +void llcan_irq_disable(const FDCAN_GlobalTypeDef *FDCANx) { + if (FDCANx == FDCAN1) { + NVIC_DisableIRQ(FDCAN1_IT0_IRQn); + NVIC_DisableIRQ(FDCAN1_IT1_IRQn); + } else if (FDCANx == FDCAN2) { + NVIC_DisableIRQ(FDCAN2_IT0_IRQn); + NVIC_DisableIRQ(FDCAN2_IT1_IRQn); + } else if (FDCANx == FDCAN3) { + NVIC_DisableIRQ(FDCAN3_IT0_IRQn); + NVIC_DisableIRQ(FDCAN3_IT1_IRQn); + } else { + } +} + +void llcan_irq_enable(const FDCAN_GlobalTypeDef *FDCANx) { + if (FDCANx == FDCAN1) { + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); + } else if (FDCANx == FDCAN2) { + NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + NVIC_EnableIRQ(FDCAN2_IT1_IRQn); + } else if (FDCANx == FDCAN3) { + NVIC_EnableIRQ(FDCAN3_IT0_IRQn); + NVIC_EnableIRQ(FDCAN3_IT1_IRQn); + } else { + } +} + +bool llcan_init(FDCAN_GlobalTypeDef *FDCANx) { + uint32_t can_number = CAN_NUM_FROM_CANIF(FDCANx); + bool ret = fdcan_request_init(FDCANx); + + if (ret) { + // Enable config change + FDCANx->CCCR |= FDCAN_CCCR_CCE; + // Enable automatic retransmission + FDCANx->CCCR &= ~(FDCAN_CCCR_DAR); + // Enable transmission pause feature + FDCANx->CCCR |= FDCAN_CCCR_TXP; + // Disable protocol exception handling + FDCANx->CCCR |= FDCAN_CCCR_PXHD; + // FD with BRS + FDCANx->CCCR |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); + + // Set TX mode to FIFO + FDCANx->TXBC &= ~(FDCAN_TXBC_TFQM); + // Configure TX element data size + FDCANx->TXESC |= 0x7U << FDCAN_TXESC_TBDS_Pos; // 64 bytes + //Configure RX FIFO0 element data size + FDCANx->RXESC |= 0x7U << FDCAN_RXESC_F0DS_Pos; + // Disable filtering, accept all valid frames received + FDCANx->XIDFC &= ~(FDCAN_XIDFC_LSE); // No extended filters + FDCANx->SIDFC &= ~(FDCAN_SIDFC_LSS); // No standard filters + FDCANx->GFC &= ~(FDCAN_GFC_RRFE); // Accept extended remote frames + FDCANx->GFC &= ~(FDCAN_GFC_RRFS); // Accept standard remote frames + FDCANx->GFC &= ~(FDCAN_GFC_ANFE); // Accept extended frames to FIFO 0 + FDCANx->GFC &= ~(FDCAN_GFC_ANFS); // Accept standard frames to FIFO 0 + + uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); + uint32_t TxFIFOSA = RxFIFO0SA + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); + + // RX FIFO 0 + FDCANx->RXF0C |= (FDCAN_RX_FIFO_0_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_RXF0C_F0SA_Pos; + FDCANx->RXF0C |= FDCAN_RX_FIFO_0_EL_CNT << FDCAN_RXF0C_F0S_Pos; + // RX FIFO 0 switch to non-blocking (overwrite) mode + FDCANx->RXF0C |= FDCAN_RXF0C_F0OM; + + // TX FIFO (mode set earlier) + FDCANx->TXBC |= (FDCAN_TX_FIFO_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_TXBC_TBSA_Pos; + FDCANx->TXBC |= FDCAN_TX_FIFO_EL_CNT << FDCAN_TXBC_TFQS_Pos; + + // Flush allocated RAM + uint32_t EndAddress = TxFIFOSA + (FDCAN_TX_FIFO_EL_CNT * FDCAN_TX_FIFO_EL_SIZE); + for (uint32_t RAMcounter = RxFIFO0SA; RAMcounter < EndAddress; RAMcounter += 4U) { + *(uint32_t *)(RAMcounter) = 0x00000000; + } + + // Enable both interrupts for each module + FDCANx->ILE = (FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1); + + FDCANx->IE &= 0x0U; // Reset all interrupts + // Messages for INT0 + FDCANx->IE |= FDCAN_IE_RF0NE; // Rx FIFO 0 new message + FDCANx->IE |= FDCAN_IE_PEDE | FDCAN_IE_PEAE | FDCAN_IE_BOE | FDCAN_IE_EPE | FDCAN_IE_RF0LE; + + // Messages for INT1 (Only TFE works??) + FDCANx->ILS |= FDCAN_ILS_TFEL; + FDCANx->IE |= FDCAN_IE_TFEE; // Tx FIFO empty + + ret = fdcan_exit_init(FDCANx); + if(!ret) { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (2)!\n"); + } + + llcan_irq_enable(FDCANx); + + } else { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (1)!\n"); + } + return ret; +} + +void llcan_clear_send(FDCAN_GlobalTypeDef *FDCANx) { + // from datasheet: "Transmit cancellation is not intended for Tx FIFO operation." + // so we need to clear pending transmission manually by resetting FDCAN core + FDCANx->IR |= 0x3FCFFFFFU; // clear all interrupts + bool ret = llcan_init(FDCANx); + UNUSED(ret); +} diff --git a/board/stm32h7/llfdcan.h b/board/stm32h7/llfdcan.h index 64a40bd072e..aa94b974909 100644 --- a/board/stm32h7/llfdcan.h +++ b/board/stm32h7/llfdcan.h @@ -1,227 +1,41 @@ -#include "llfdcan_declarations.h" - -// kbps multiplied by 10 -const uint32_t speeds[SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U}; -const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U, 20000U, 50000U}; - -static bool fdcan_request_init(FDCAN_GlobalTypeDef *FDCANx) { - bool ret = true; - // Exit from sleep mode - FDCANx->CCCR &= ~(FDCAN_CCCR_CSR); - while ((FDCANx->CCCR & FDCAN_CCCR_CSA) == FDCAN_CCCR_CSA); - - // Request init - uint32_t timeout_counter = 0U; - FDCANx->CCCR |= FDCAN_CCCR_INIT; - while ((FDCANx->CCCR & FDCAN_CCCR_INIT) == 0U) { - // Delay for about 1ms - delay(10000); - timeout_counter++; - - if (timeout_counter >= CAN_INIT_TIMEOUT_MS){ - ret = false; - break; - } - } - return ret; -} - -static bool fdcan_exit_init(FDCAN_GlobalTypeDef *FDCANx) { - bool ret = true; - - FDCANx->CCCR &= ~(FDCAN_CCCR_INIT); - uint32_t timeout_counter = 0U; - while ((FDCANx->CCCR & FDCAN_CCCR_INIT) != 0U) { - // Delay for about 1ms - delay(10000); - timeout_counter++; - - if (timeout_counter >= CAN_INIT_TIMEOUT_MS) { - ret = false; - break; - } - } - return ret; -} - -bool llcan_set_speed(FDCAN_GlobalTypeDef *FDCANx, uint32_t speed, uint32_t data_speed, bool non_iso, bool loopback, bool silent) { - UNUSED(speed); - bool ret = fdcan_request_init(FDCANx); - - if (ret) { - // Enable config change - FDCANx->CCCR |= FDCAN_CCCR_CCE; - - //Reset operation mode to Normal - FDCANx->CCCR &= ~(FDCAN_CCCR_TEST); - FDCANx->TEST &= ~(FDCAN_TEST_LBCK); - FDCANx->CCCR &= ~(FDCAN_CCCR_MON); - FDCANx->CCCR &= ~(FDCAN_CCCR_ASM); - FDCANx->CCCR &= ~(FDCAN_CCCR_NISO); - - // TODO: add as a separate safety mode - // Enable ASM restricted operation(for debug or automatic bitrate switching) - //FDCANx->CCCR |= FDCAN_CCCR_ASM; - - uint8_t prescaler = BITRATE_PRESCALER; - if (speed < 2500U) { - // The only way to support speeds lower than 250Kbit/s (down to 10Kbit/s) - prescaler = BITRATE_PRESCALER * 16U; - } - - // Set the nominal bit timing values - uint32_t tq = CAN_QUANTA(speed, prescaler); - uint32_t sp = CAN_SP_NOMINAL; - uint32_t seg1 = CAN_SEG1(tq, sp); - uint32_t seg2 = CAN_SEG2(tq, sp); - uint8_t sjw = MIN(127U, seg2); - - FDCANx->NBTP = (((sjw & 0x7FUL)-1U)<DBTP = (((sjw & 0xFUL)-1U)<CCCR |= FDCAN_CCCR_NISO; - } - - // Silent loopback is known as internal loopback in the docs - if (loopback) { - FDCANx->CCCR |= FDCAN_CCCR_TEST; - FDCANx->TEST |= FDCAN_TEST_LBCK; - FDCANx->CCCR |= FDCAN_CCCR_MON; - } - // Silent is known as bus monitoring in the docs - if (silent) { - FDCANx->CCCR |= FDCAN_CCCR_MON; - } - ret = fdcan_exit_init(FDCANx); - if (!ret) { - print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (2)\n"); - } - } else { - print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (1)\n"); - } - return ret; -} - -void llcan_irq_disable(const FDCAN_GlobalTypeDef *FDCANx) { - if (FDCANx == FDCAN1) { - NVIC_DisableIRQ(FDCAN1_IT0_IRQn); - NVIC_DisableIRQ(FDCAN1_IT1_IRQn); - } else if (FDCANx == FDCAN2) { - NVIC_DisableIRQ(FDCAN2_IT0_IRQn); - NVIC_DisableIRQ(FDCAN2_IT1_IRQn); - } else if (FDCANx == FDCAN3) { - NVIC_DisableIRQ(FDCAN3_IT0_IRQn); - NVIC_DisableIRQ(FDCAN3_IT1_IRQn); - } else { - } -} - -void llcan_irq_enable(const FDCAN_GlobalTypeDef *FDCANx) { - if (FDCANx == FDCAN1) { - NVIC_EnableIRQ(FDCAN1_IT0_IRQn); - NVIC_EnableIRQ(FDCAN1_IT1_IRQn); - } else if (FDCANx == FDCAN2) { - NVIC_EnableIRQ(FDCAN2_IT0_IRQn); - NVIC_EnableIRQ(FDCAN2_IT1_IRQn); - } else if (FDCANx == FDCAN3) { - NVIC_EnableIRQ(FDCAN3_IT0_IRQn); - NVIC_EnableIRQ(FDCAN3_IT1_IRQn); - } else { - } -} - -bool llcan_init(FDCAN_GlobalTypeDef *FDCANx) { - uint32_t can_number = CAN_NUM_FROM_CANIF(FDCANx); - bool ret = fdcan_request_init(FDCANx); - - if (ret) { - // Enable config change - FDCANx->CCCR |= FDCAN_CCCR_CCE; - // Enable automatic retransmission - FDCANx->CCCR &= ~(FDCAN_CCCR_DAR); - // Enable transmission pause feature - FDCANx->CCCR |= FDCAN_CCCR_TXP; - // Disable protocol exception handling - FDCANx->CCCR |= FDCAN_CCCR_PXHD; - // FD with BRS - FDCANx->CCCR |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); - - // Set TX mode to FIFO - FDCANx->TXBC &= ~(FDCAN_TXBC_TFQM); - // Configure TX element data size - FDCANx->TXESC |= 0x7U << FDCAN_TXESC_TBDS_Pos; // 64 bytes - //Configure RX FIFO0 element data size - FDCANx->RXESC |= 0x7U << FDCAN_RXESC_F0DS_Pos; - // Disable filtering, accept all valid frames received - FDCANx->XIDFC &= ~(FDCAN_XIDFC_LSE); // No extended filters - FDCANx->SIDFC &= ~(FDCAN_SIDFC_LSS); // No standard filters - FDCANx->GFC &= ~(FDCAN_GFC_RRFE); // Accept extended remote frames - FDCANx->GFC &= ~(FDCAN_GFC_RRFS); // Accept standard remote frames - FDCANx->GFC &= ~(FDCAN_GFC_ANFE); // Accept extended frames to FIFO 0 - FDCANx->GFC &= ~(FDCAN_GFC_ANFS); // Accept standard frames to FIFO 0 - - uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); - uint32_t TxFIFOSA = RxFIFO0SA + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); - - // RX FIFO 0 - FDCANx->RXF0C |= (FDCAN_RX_FIFO_0_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_RXF0C_F0SA_Pos; - FDCANx->RXF0C |= FDCAN_RX_FIFO_0_EL_CNT << FDCAN_RXF0C_F0S_Pos; - // RX FIFO 0 switch to non-blocking (overwrite) mode - FDCANx->RXF0C |= FDCAN_RXF0C_F0OM; - - // TX FIFO (mode set earlier) - FDCANx->TXBC |= (FDCAN_TX_FIFO_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_TXBC_TBSA_Pos; - FDCANx->TXBC |= FDCAN_TX_FIFO_EL_CNT << FDCAN_TXBC_TFQS_Pos; - - // Flush allocated RAM - uint32_t EndAddress = TxFIFOSA + (FDCAN_TX_FIFO_EL_CNT * FDCAN_TX_FIFO_EL_SIZE); - for (uint32_t RAMcounter = RxFIFO0SA; RAMcounter < EndAddress; RAMcounter += 4U) { - *(uint32_t *)(RAMcounter) = 0x00000000; - } - - // Enable both interrupts for each module - FDCANx->ILE = (FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1); - - FDCANx->IE &= 0x0U; // Reset all interrupts - // Messages for INT0 - FDCANx->IE |= FDCAN_IE_RF0NE; // Rx FIFO 0 new message - FDCANx->IE |= FDCAN_IE_PEDE | FDCAN_IE_PEAE | FDCAN_IE_BOE | FDCAN_IE_EPE | FDCAN_IE_RF0LE; - - // Messages for INT1 (Only TFE works??) - FDCANx->ILS |= FDCAN_ILS_TFEL; - FDCANx->IE |= FDCAN_IE_TFEE; // Tx FIFO empty - - ret = fdcan_exit_init(FDCANx); - if(!ret) { - print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (2)!\n"); - } - - llcan_irq_enable(FDCANx); - - } else { - print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (1)!\n"); - } - return ret; -} - -void llcan_clear_send(FDCAN_GlobalTypeDef *FDCANx) { - // from datasheet: "Transmit cancellation is not intended for Tx FIFO operation." - // so we need to clear pending transmission manually by resetting FDCAN core - FDCANx->IR |= 0x3FCFFFFFU; // clear all interrupts - bool ret = llcan_init(FDCANx); - UNUSED(ret); -} +#pragma once + +#define CAN_PCLK 80000U +#define BITRATE_PRESCALER 2U +#define CAN_SP_NOMINAL 80U +#define CAN_SP_DATA_2M 80U +#define CAN_SP_DATA_5M 75U +#define CAN_QUANTA(speed, prescaler) (CAN_PCLK / ((speed) / 10U * (prescaler))) +#define CAN_SEG1(tq, sp) (((tq) * (sp) / 100U)- 1U) +#define CAN_SEG2(tq, sp) ((tq) * (100U - (sp)) / 100U) + +#define FDCAN_START_ADDRESS 0x4000AC00UL +#define FDCAN_OFFSET 3384UL +#define FDCAN_OFFSET_W 846UL + +#define FDCAN_RX_FIFO_0_EL_CNT 46UL +#define FDCAN_RX_FIFO_0_HEAD_SIZE 8UL +#define FDCAN_RX_FIFO_0_DATA_SIZE 64UL +#define FDCAN_RX_FIFO_0_EL_SIZE (FDCAN_RX_FIFO_0_HEAD_SIZE + FDCAN_RX_FIFO_0_DATA_SIZE) +#define FDCAN_RX_FIFO_0_EL_W_SIZE (FDCAN_RX_FIFO_0_EL_SIZE / 4UL) +#define FDCAN_RX_FIFO_0_OFFSET 0UL + +#define FDCAN_TX_FIFO_EL_CNT 1UL +#define FDCAN_TX_FIFO_HEAD_SIZE 8UL +#define FDCAN_TX_FIFO_DATA_SIZE 64UL +#define FDCAN_TX_FIFO_EL_SIZE (FDCAN_TX_FIFO_HEAD_SIZE + FDCAN_TX_FIFO_DATA_SIZE) +#define FDCAN_TX_FIFO_OFFSET (FDCAN_RX_FIFO_0_OFFSET + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_W_SIZE)) + +#define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? "FDCAN1" : (((CAN_DEV) == FDCAN2) ? "FDCAN2" : "FDCAN3")) +#define CAN_NUM_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? 0UL : (((CAN_DEV) == FDCAN2) ? 1UL : 2UL)) + +#define SPEEDS_ARRAY_SIZE 8 +extern const uint32_t speeds[SPEEDS_ARRAY_SIZE]; +#define DATA_SPEEDS_ARRAY_SIZE 10 +extern const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE]; + +bool llcan_set_speed(FDCAN_GlobalTypeDef *FDCANx, uint32_t speed, uint32_t data_speed, bool non_iso, bool loopback, bool silent); +void llcan_irq_disable(const FDCAN_GlobalTypeDef *FDCANx); +void llcan_irq_enable(const FDCAN_GlobalTypeDef *FDCANx); +bool llcan_init(FDCAN_GlobalTypeDef *FDCANx); +void llcan_clear_send(FDCAN_GlobalTypeDef *FDCANx); diff --git a/board/stm32h7/llfdcan_declarations.h b/board/stm32h7/llfdcan_declarations.h deleted file mode 100644 index 793849011eb..00000000000 --- a/board/stm32h7/llfdcan_declarations.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -// SAE J2284-4 document specifies a bus-line network running at 2 Mbit/s -// SAE J2284-5 document specifies a point-to-point communication running at 5 Mbit/s - -#define CAN_PCLK 80000U // KHz, sourced from PLL1Q -#define BITRATE_PRESCALER 2U // Valid from 250Kbps to 5Mbps with 80Mhz clock -#define CAN_SP_NOMINAL 80U // 80% for both SAE J2284-4 and SAE J2284-5 -#define CAN_SP_DATA_2M 80U // 80% for SAE J2284-4 -#define CAN_SP_DATA_5M 75U // 75% for SAE J2284-5 -#define CAN_QUANTA(speed, prescaler) (CAN_PCLK / ((speed) / 10U * (prescaler))) -#define CAN_SEG1(tq, sp) (((tq) * (sp) / 100U)- 1U) -#define CAN_SEG2(tq, sp) ((tq) * (100U - (sp)) / 100U) - -// FDCAN core settings -#define FDCAN_START_ADDRESS 0x4000AC00UL -#define FDCAN_OFFSET 3384UL // bytes for each FDCAN module, equally -#define FDCAN_OFFSET_W 846UL // words for each FDCAN module, equally - -// FDCAN_RX_FIFO_0_EL_CNT + FDCAN_TX_FIFO_EL_CNT can't exceed 47 elements (47 * 72 bytes = 3,384 bytes) per FDCAN module - -// RX FIFO 0 -#define FDCAN_RX_FIFO_0_EL_CNT 46UL -#define FDCAN_RX_FIFO_0_HEAD_SIZE 8UL // bytes -#define FDCAN_RX_FIFO_0_DATA_SIZE 64UL // bytes -#define FDCAN_RX_FIFO_0_EL_SIZE (FDCAN_RX_FIFO_0_HEAD_SIZE + FDCAN_RX_FIFO_0_DATA_SIZE) -#define FDCAN_RX_FIFO_0_EL_W_SIZE (FDCAN_RX_FIFO_0_EL_SIZE / 4UL) -#define FDCAN_RX_FIFO_0_OFFSET 0UL - -// TX FIFO -#define FDCAN_TX_FIFO_EL_CNT 1UL -#define FDCAN_TX_FIFO_HEAD_SIZE 8UL // bytes -#define FDCAN_TX_FIFO_DATA_SIZE 64UL // bytes -#define FDCAN_TX_FIFO_EL_SIZE (FDCAN_TX_FIFO_HEAD_SIZE + FDCAN_TX_FIFO_DATA_SIZE) -#define FDCAN_TX_FIFO_OFFSET (FDCAN_RX_FIFO_0_OFFSET + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_W_SIZE)) - -#define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? "FDCAN1" : (((CAN_DEV) == FDCAN2) ? "FDCAN2" : "FDCAN3")) -#define CAN_NUM_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? 0UL : (((CAN_DEV) == FDCAN2) ? 1UL : 2UL)) - - -void print(const char *a); - -// kbps multiplied by 10 -#define SPEEDS_ARRAY_SIZE 8 -extern const uint32_t speeds[SPEEDS_ARRAY_SIZE]; -#define DATA_SPEEDS_ARRAY_SIZE 10 -extern const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE]; - -bool llcan_set_speed(FDCAN_GlobalTypeDef *FDCANx, uint32_t speed, uint32_t data_speed, bool non_iso, bool loopback, bool silent); -void llcan_irq_disable(const FDCAN_GlobalTypeDef *FDCANx); -void llcan_irq_enable(const FDCAN_GlobalTypeDef *FDCANx); -bool llcan_init(FDCAN_GlobalTypeDef *FDCANx); -void llcan_clear_send(FDCAN_GlobalTypeDef *FDCANx); diff --git a/board/stm32h7/llflash.c b/board/stm32h7/llflash.c new file mode 100644 index 00000000000..41740451b88 --- /dev/null +++ b/board/stm32h7/llflash.c @@ -0,0 +1,37 @@ +#include "board/config.h" +#include "board/stm32h7/llflash.h" + +bool flash_is_locked(void) { + return (FLASH->CR1 & FLASH_CR_LOCK) != 0U; +} + +void flash_unlock(void) { + FLASH->KEYR1 = 0x45670123U; + FLASH->KEYR1 = 0xCDEF89ABU; +} + +bool flash_erase_sector(uint8_t sector, bool unlocked) { + // don't erase the bootloader(sector 0) + bool ret = false; + if ((sector != 0U) && (sector < 8U) && unlocked) { + FLASH->CR1 = ((uint32_t)sector << 8U) | FLASH_CR_SER; + FLASH->CR1 |= FLASH_CR_START; + while ((FLASH->SR1 & FLASH_SR_QW) != 0U) {} + ret = true; + } + return ret; +} + +void flash_write_word(void *prog_ptr, uint32_t data) { + uint32_t *pp = prog_ptr; + FLASH->CR1 |= FLASH_CR_PG; + *pp = data; + while ((FLASH->SR1 & FLASH_SR_QW) != 0U) {} +} + +void flush_write_buffer(void) { + if ((FLASH->SR1 & FLASH_SR_WBNE) != 0U) { + FLASH->CR1 |= FLASH_CR_FW; + while ((FLASH->SR1 & FLASH_CR_FW) != 0U) {} + } +} diff --git a/board/stm32h7/llflash.h b/board/stm32h7/llflash.h index b95011a9ed8..40d514ca0de 100644 --- a/board/stm32h7/llflash.h +++ b/board/stm32h7/llflash.h @@ -1,33 +1,10 @@ -bool flash_is_locked(void) { - return (FLASH->CR1 & FLASH_CR_LOCK); -} +#pragma once -void flash_unlock(void) { - FLASH->KEYR1 = 0x45670123; - FLASH->KEYR1 = 0xCDEF89AB; -} +#include +#include -bool flash_erase_sector(uint8_t sector, bool unlocked) { - // don't erase the bootloader(sector 0) - if (sector != 0 && sector < 8 && unlocked) { - FLASH->CR1 = (sector << 8) | FLASH_CR_SER; - FLASH->CR1 |= FLASH_CR_START; - while (FLASH->SR1 & FLASH_SR_QW); - return true; - } - return false; -} - -void flash_write_word(void *prog_ptr, uint32_t data) { - uint32_t *pp = prog_ptr; - FLASH->CR1 |= FLASH_CR_PG; - *pp = data; - while (FLASH->SR1 & FLASH_SR_QW); -} - -void flush_write_buffer(void) { - if (FLASH->SR1 & FLASH_SR_WBNE) { - FLASH->CR1 |= FLASH_CR_FW; - while (FLASH->SR1 & FLASH_CR_FW); - } -} +bool flash_is_locked(void); +void flash_unlock(void); +bool flash_erase_sector(uint8_t sector, bool unlocked); +void flash_write_word(void *prog_ptr, uint32_t data); +void flush_write_buffer(void); diff --git a/board/stm32h7/lli2c.c b/board/stm32h7/lli2c.c new file mode 100644 index 00000000000..315720a5574 --- /dev/null +++ b/board/stm32h7/lli2c.c @@ -0,0 +1,170 @@ +#include "board/config.h" +#include "board/stm32h7/lli2c.h" + +// TODO: this driver relies heavily on polling, +// if we want it to be more async, we should use interrupts + +#define I2C_RETRY_COUNT 10U +#define I2C_TIMEOUT_US 100000U + +bool i2c_status_wait(const volatile uint32_t *reg, uint32_t mask, uint32_t val) { + uint32_t start_time = microsecond_timer_get(); + while(((*reg & mask) != val) && (get_ts_elapsed(microsecond_timer_get(), start_time) < I2C_TIMEOUT_US)) {} + return ((*reg & mask) == val); +} + +void i2c_reset(I2C_TypeDef *I2C) { + // peripheral reset + register_clear_bits(&I2C->CR1, I2C_CR1_PE); + while ((I2C->CR1 & I2C_CR1_PE) != 0U) {} + register_set_bits(&I2C->CR1, I2C_CR1_PE); +} + +bool i2c_write_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t value) { + bool ret = false; + + // Setup transfer and send START + addr + for (uint32_t i = 0U; i < I2C_RETRY_COUNT; i++) { + register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); + I2C->CR2 = ((uint32_t)addr << 1U) & I2C_CR2_SADD_Msk; + register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); + register_set_bits(&I2C->CR2, I2C_CR2_AUTOEND); + I2C->CR2 |= 2UL << I2C_CR2_NBYTES_Pos; + + I2C->CR2 |= I2C_CR2_START; + if(!i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U)) { + continue; + } + + // check if we lost arbitration + if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { + register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); + } else { + ret = true; + break; + } + } + + if (!ret) { + goto end; + } + + // Send data + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); + if(!ret) { + goto end; + } + I2C->TXDR = reg; + + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); + if(!ret) { + goto end; + } + I2C->TXDR = value; + +end: + return ret; +} + +bool i2c_read_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t *value) { + bool ret = false; + + // Setup transfer and send START + addr + for (uint32_t i = 0U; i < I2C_RETRY_COUNT; i++) { + register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); + I2C->CR2 = ((uint32_t)addr << 1U) & I2C_CR2_SADD_Msk; + register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); + register_clear_bits(&I2C->CR2, I2C_CR2_AUTOEND); + I2C->CR2 |= 1UL << I2C_CR2_NBYTES_Pos; + + I2C->CR2 |= I2C_CR2_START; + if(!i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U)) { + continue; + } + + // check if we lost arbitration + if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { + register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); + } else { + ret = true; + break; + } + } + + if (!ret) { + goto end; + } + + // Send data + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); + if(!ret) { + goto end; + } + I2C->TXDR = reg; + + // Restart + I2C->CR2 = (((addr << 1) | 0x1U) & I2C_CR2_SADD_Msk) | (1UL << I2C_CR2_NBYTES_Pos) | I2C_CR2_RD_WRN | I2C_CR2_START; + ret = i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U); + if(!ret) { + goto end; + } + + // check if we lost arbitration + if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { + register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); + ret = false; + goto end; + } + + // Read data + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_RXNE, I2C_ISR_RXNE); + if(!ret) { + goto end; + } + *value = I2C->RXDR; + + // Stop + I2C->CR2 |= I2C_CR2_STOP; + +end: + + if (!ret) { + i2c_reset(I2C); + } + + return ret; +} + +bool i2c_set_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits) { + uint8_t value; + bool ret = i2c_read_reg(I2C, address, regis, &value); + if(ret) { + ret = i2c_write_reg(I2C, address, regis, value | bits); + } + return ret; +} + +bool i2c_clear_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits) { + uint8_t value; + bool ret = i2c_read_reg(I2C, address, regis, &value); + if (ret) { + ret = i2c_write_reg(I2C, address, regis, value & (uint8_t) (~bits)); + } + return ret; +} + +bool i2c_set_reg_mask(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t value, uint8_t mask) { + uint8_t old_value; + bool ret = i2c_read_reg(I2C, address, regis, &old_value); + if(ret) { + ret = i2c_write_reg(I2C, address, regis, (old_value & (uint8_t) (~mask)) | (value & mask)); + } + return ret; +} + +void i2c_init(I2C_TypeDef *I2C) { + // 100kHz clock speed + I2C->TIMINGR = 0x107075B0; + + i2c_reset(I2C); +} diff --git a/board/stm32h7/lli2c.h b/board/stm32h7/lli2c.h index 5d79beb653e..e30353b79d9 100644 --- a/board/stm32h7/lli2c.h +++ b/board/stm32h7/lli2c.h @@ -1,167 +1,10 @@ -// TODO: this driver relies heavily on polling, -// if we want it to be more async, we should use interrupts - -#define I2C_RETRY_COUNT 10U -#define I2C_TIMEOUT_US 100000U - -bool i2c_status_wait(const volatile uint32_t *reg, uint32_t mask, uint32_t val) { - uint32_t start_time = microsecond_timer_get(); - while(((*reg & mask) != val) && (get_ts_elapsed(microsecond_timer_get(), start_time) < I2C_TIMEOUT_US)); - return ((*reg & mask) == val); -} - -void i2c_reset(I2C_TypeDef *I2C) { - // peripheral reset - register_clear_bits(&I2C->CR1, I2C_CR1_PE); - while ((I2C->CR1 & I2C_CR1_PE) != 0U); - register_set_bits(&I2C->CR1, I2C_CR1_PE); -} - -bool i2c_write_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t value) { - bool ret = false; - - // Setup transfer and send START + addr - for (uint32_t i = 0U; i < I2C_RETRY_COUNT; i++) { - register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); - I2C->CR2 = ((uint32_t)addr << 1U) & I2C_CR2_SADD_Msk; - register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); - register_set_bits(&I2C->CR2, I2C_CR2_AUTOEND); - I2C->CR2 |= 2UL << I2C_CR2_NBYTES_Pos; - - I2C->CR2 |= I2C_CR2_START; - if(!i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U)) { - continue; - } - - // check if we lost arbitration - if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { - register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); - } else { - ret = true; - break; - } - } - - if (!ret) { - goto end; - } - - // Send data - ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); - if(!ret) { - goto end; - } - I2C->TXDR = reg; - - ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); - if(!ret) { - goto end; - } - I2C->TXDR = value; - -end: - return ret; -} - -bool i2c_read_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t *value) { - bool ret = false; - - // Setup transfer and send START + addr - for (uint32_t i = 0U; i < I2C_RETRY_COUNT; i++) { - register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); - I2C->CR2 = ((uint32_t)addr << 1U) & I2C_CR2_SADD_Msk; - register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); - register_clear_bits(&I2C->CR2, I2C_CR2_AUTOEND); - I2C->CR2 |= 1UL << I2C_CR2_NBYTES_Pos; - - I2C->CR2 |= I2C_CR2_START; - if(!i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U)) { - continue; - } - - // check if we lost arbitration - if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { - register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); - } else { - ret = true; - break; - } - } - - if (!ret) { - goto end; - } - - // Send data - ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); - if(!ret) { - goto end; - } - I2C->TXDR = reg; - - // Restart - I2C->CR2 = (((addr << 1) | 0x1U) & I2C_CR2_SADD_Msk) | (1UL << I2C_CR2_NBYTES_Pos) | I2C_CR2_RD_WRN | I2C_CR2_START; - ret = i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U); - if(!ret) { - goto end; - } - - // check if we lost arbitration - if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { - register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); - ret = false; - goto end; - } - - // Read data - ret = i2c_status_wait(&I2C->ISR, I2C_ISR_RXNE, I2C_ISR_RXNE); - if(!ret) { - goto end; - } - *value = I2C->RXDR; - - // Stop - I2C->CR2 |= I2C_CR2_STOP; - -end: - - if (!ret) { - i2c_reset(I2C); - } - - return ret; -} - -bool i2c_set_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits) { - uint8_t value; - bool ret = i2c_read_reg(I2C, address, regis, &value); - if(ret) { - ret = i2c_write_reg(I2C, address, regis, value | bits); - } - return ret; -} - -bool i2c_clear_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits) { - uint8_t value; - bool ret = i2c_read_reg(I2C, address, regis, &value); - if (ret) { - ret = i2c_write_reg(I2C, address, regis, value & (uint8_t) (~bits)); - } - return ret; -} - -bool i2c_set_reg_mask(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t value, uint8_t mask) { - uint8_t old_value; - bool ret = i2c_read_reg(I2C, address, regis, &old_value); - if(ret) { - ret = i2c_write_reg(I2C, address, regis, (old_value & (uint8_t) (~mask)) | (value & mask)); - } - return ret; -} - -void i2c_init(I2C_TypeDef *I2C) { - // 100kHz clock speed - I2C->TIMINGR = 0x107075B0; - - i2c_reset(I2C); -} +#pragma once + +bool i2c_status_wait(const volatile uint32_t *reg, uint32_t mask, uint32_t val); +void i2c_reset(I2C_TypeDef *I2C); +bool i2c_write_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t value); +bool i2c_read_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t *value); +bool i2c_set_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits); +bool i2c_clear_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits); +bool i2c_set_reg_mask(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t value, uint8_t mask); +void i2c_init(I2C_TypeDef *I2C); diff --git a/board/stm32h7/llspi.c b/board/stm32h7/llspi.c new file mode 100644 index 00000000000..c139008b77f --- /dev/null +++ b/board/stm32h7/llspi.c @@ -0,0 +1,108 @@ +#include "board/config.h" +#include "board/stm32h7/llspi.h" +#include "board/drivers/spi.h" + +// master -> panda DMA start +void llspi_mosi_dma(uint8_t *addr, int len) { + // disable DMA + SPI + register_clear_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN); + DMA2_Stream2->CR &= ~DMA_SxCR_EN; + register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE); + + // drain the bus + while ((SPI4->SR & SPI_SR_RXP) != 0U) { + volatile uint8_t dat = SPI4->RXDR; + (void)dat; + } + + // clear all pending + SPI4->IFCR |= (0x1FFU << 3U); + register_set(&(SPI4->IER), 0, 0x3FFU); + + // setup destination and length + register_set(&(DMA2_Stream2->M0AR), (uint32_t)addr, 0xFFFFFFFFU); + DMA2_Stream2->NDTR = len; + + // enable DMA + SPI + DMA2_Stream2->CR |= DMA_SxCR_EN; + register_set_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN); + register_set_bits(&(SPI4->CR1), SPI_CR1_SPE); +} + +// panda -> master DMA start +void llspi_miso_dma(uint8_t *addr, int len) { + // disable DMA + SPI + DMA2_Stream3->CR &= ~DMA_SxCR_EN; + register_clear_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); + register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE); + + // setup source and length + register_set(&(DMA2_Stream3->M0AR), (uint32_t)addr, 0xFFFFFFFFU); + DMA2_Stream3->NDTR = len; + + // clear under-run while we were reading + SPI4->IFCR |= (0x1FFU << 3U); + + // setup interrupt on TXC + register_set(&(SPI4->IER), (1U << SPI_IER_EOTIE_Pos), 0x3FFU); + + // enable DMA + SPI + register_set_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); + DMA2_Stream3->CR |= DMA_SxCR_EN; + register_set_bits(&(SPI4->CR1), SPI_CR1_SPE); +} + +static bool spi_tx_dma_done = false; + +// master -> panda DMA finished +static void DMA2_Stream2_IRQ_Handler(void) { + // Clear interrupt flag + DMA2->LIFCR = DMA_LIFCR_CTCIF2; + spi_rx_done(); +} + +// panda -> master DMA finished +static void DMA2_Stream3_IRQ_Handler(void) { + ENTER_CRITICAL(); + DMA2->LIFCR = DMA_LIFCR_CTCIF3; + spi_tx_dma_done = true; + EXIT_CRITICAL(); +} + +// panda TX finished +static void SPI4_IRQ_Handler(void) { + // clear flag + SPI4->IFCR |= (0x1FFU << 3U); + + if (spi_tx_dma_done && ((SPI4->SR & SPI_SR_TXC) != 0U)) { + spi_tx_dma_done = false; + spi_tx_done(false); + } +} + +void llspi_init(void) { + REGISTER_INTERRUPT(SPI4_IRQn, SPI4_IRQ_Handler, (SPI_IRQ_RATE * 2U), FAULT_INTERRUPT_RATE_SPI) + REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) + REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) + + // Setup MOSI DMA + register_set(&(DMAMUX1_Channel10->CCR), 83U, 0xFFFFFFFFU); + register_set(&(DMA2_Stream2->CR), (DMA_SxCR_MINC | DMA_SxCR_TCIE), 0x1E077EFEU); + register_set(&(DMA2_Stream2->PAR), (uint32_t)&(SPI4->RXDR), 0xFFFFFFFFU); + + // Setup MISO DMA, memory -> peripheral + register_set(&(DMAMUX1_Channel11->CCR), 84U, 0xFFFFFFFFU); + register_set(&(DMA2_Stream3->CR), (DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE), 0x1E077EFEU); + register_set(&(DMA2_Stream3->PAR), (uint32_t)&(SPI4->TXDR), 0xFFFFFFFFU); + + // Enable SPI + register_set(&(SPI4->IER), 0, 0x3FFU); + register_set(&(SPI4->CFG1), (7U << SPI_CFG1_DSIZE_Pos), SPI_CFG1_DSIZE_Msk); + register_set(&(SPI4->UDRDR), 0xcd, 0xFFFFU); // set under-run value for debugging + register_set(&(SPI4->CR1), SPI_CR1_SPE, 0xFFFFU); + register_set(&(SPI4->CR2), 0, 0xFFFFU); + + NVIC_EnableIRQ(DMA2_Stream2_IRQn); + NVIC_EnableIRQ(DMA2_Stream3_IRQn); + NVIC_EnableIRQ(SPI4_IRQn); +} diff --git a/board/stm32h7/llspi.h b/board/stm32h7/llspi.h index 05f8e22f9a8..2424aeba2f2 100644 --- a/board/stm32h7/llspi.h +++ b/board/stm32h7/llspi.h @@ -1,107 +1,7 @@ -// master -> panda DMA start -void llspi_mosi_dma(uint8_t *addr, int len) { - // disable DMA + SPI - register_clear_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN); - DMA2_Stream2->CR &= ~DMA_SxCR_EN; - register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE); +#pragma once - // drain the bus - while ((SPI4->SR & SPI_SR_RXP) != 0U) { - volatile uint8_t dat = SPI4->RXDR; - (void)dat; - } +#include - // clear all pending - SPI4->IFCR |= (0x1FFU << 3U); - register_set(&(SPI4->IER), 0, 0x3FFU); - - // setup destination and length - register_set(&(DMA2_Stream2->M0AR), (uint32_t)addr, 0xFFFFFFFFU); - DMA2_Stream2->NDTR = len; - - // enable DMA + SPI - DMA2_Stream2->CR |= DMA_SxCR_EN; - register_set_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN); - register_set_bits(&(SPI4->CR1), SPI_CR1_SPE); -} - -// panda -> master DMA start -void llspi_miso_dma(uint8_t *addr, int len) { - // disable DMA + SPI - DMA2_Stream3->CR &= ~DMA_SxCR_EN; - register_clear_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); - register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE); - - // setup source and length - register_set(&(DMA2_Stream3->M0AR), (uint32_t)addr, 0xFFFFFFFFU); - DMA2_Stream3->NDTR = len; - - // clear under-run while we were reading - SPI4->IFCR |= (0x1FFU << 3U); - - // setup interrupt on TXC - register_set(&(SPI4->IER), (1U << SPI_IER_EOTIE_Pos), 0x3FFU); - - // enable DMA + SPI - register_set_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); - DMA2_Stream3->CR |= DMA_SxCR_EN; - register_set_bits(&(SPI4->CR1), SPI_CR1_SPE); -} - -static bool spi_tx_dma_done = false; -// master -> panda DMA finished -static void DMA2_Stream2_IRQ_Handler(void) { - // Clear interrupt flag - DMA2->LIFCR = DMA_LIFCR_CTCIF2; - - spi_rx_done(); -} - -// panda -> master DMA finished -static void DMA2_Stream3_IRQ_Handler(void) { - ENTER_CRITICAL(); - - DMA2->LIFCR = DMA_LIFCR_CTCIF3; - spi_tx_dma_done = true; - - EXIT_CRITICAL(); -} - -// panda TX finished -static void SPI4_IRQ_Handler(void) { - // clear flag - SPI4->IFCR |= (0x1FFU << 3U); - - if (spi_tx_dma_done && ((SPI4->SR & SPI_SR_TXC) != 0U)) { - spi_tx_dma_done = false; - spi_tx_done(false); - } -} - - -void llspi_init(void) { - REGISTER_INTERRUPT(SPI4_IRQn, SPI4_IRQ_Handler, (SPI_IRQ_RATE * 2U), FAULT_INTERRUPT_RATE_SPI) - REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) - REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) - - // Setup MOSI DMA - register_set(&(DMAMUX1_Channel10->CCR), 83U, 0xFFFFFFFFU); - register_set(&(DMA2_Stream2->CR), (DMA_SxCR_MINC | DMA_SxCR_TCIE), 0x1E077EFEU); - register_set(&(DMA2_Stream2->PAR), (uint32_t)&(SPI4->RXDR), 0xFFFFFFFFU); - - // Setup MISO DMA, memory -> peripheral - register_set(&(DMAMUX1_Channel11->CCR), 84U, 0xFFFFFFFFU); - register_set(&(DMA2_Stream3->CR), (DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE), 0x1E077EFEU); - register_set(&(DMA2_Stream3->PAR), (uint32_t)&(SPI4->TXDR), 0xFFFFFFFFU); - - // Enable SPI - register_set(&(SPI4->IER), 0, 0x3FFU); - register_set(&(SPI4->CFG1), (7U << SPI_CFG1_DSIZE_Pos), SPI_CFG1_DSIZE_Msk); - register_set(&(SPI4->UDRDR), 0xcd, 0xFFFFU); // set under-run value for debugging - register_set(&(SPI4->CR1), SPI_CR1_SPE, 0xFFFFU); - register_set(&(SPI4->CR2), 0, 0xFFFFU); - - NVIC_EnableIRQ(DMA2_Stream2_IRQn); - NVIC_EnableIRQ(DMA2_Stream3_IRQn); - NVIC_EnableIRQ(SPI4_IRQn); -} +void llspi_mosi_dma(uint8_t *addr, int len); +void llspi_miso_dma(uint8_t *addr, int len); +void llspi_init(void); diff --git a/board/stm32h7/lluart.c b/board/stm32h7/lluart.c new file mode 100644 index 00000000000..a5bafe11e89 --- /dev/null +++ b/board/stm32h7/lluart.c @@ -0,0 +1,107 @@ +#include "board/config.h" +#include "board/stm32h7/lluart.h" + +extern uart_ring uart_ring_som_debug; + +static void uart_rx_ring(uart_ring *q){ + // Do not read out directly if DMA enabled + ENTER_CRITICAL(); + + // Read out RX buffer + uint8_t c = q->uart->RDR; // This read after reading SR clears a bunch of interrupts + + uint16_t next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; + + if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { + // overwrite mode: drop oldest byte + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + } + + // Do not overwrite buffer data + if (next_w_ptr != q->r_ptr_rx) { + q->elems_rx[q->w_ptr_rx] = c; + q->w_ptr_rx = next_w_ptr; + if (q->callback != NULL) { + q->callback(q); + } + } + + EXIT_CRITICAL(); +} + +void uart_tx_ring(uart_ring *q){ + ENTER_CRITICAL(); + // Send out next byte of TX buffer + if (q->w_ptr_tx != q->r_ptr_tx) { + // Only send if transmit register is empty (aka last byte has been sent) + if ((q->uart->ISR & USART_ISR_TXE_TXFNF) != 0U) { + q->uart->TDR = q->elems_tx[q->r_ptr_tx]; // This clears TXE + q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; + } + + // Enable TXE interrupt if there is still data to be sent + if(q->r_ptr_tx != q->w_ptr_tx){ + q->uart->CR1 |= USART_CR1_TXEIE; + } else { + q->uart->CR1 &= ~USART_CR1_TXEIE; + } + } + EXIT_CRITICAL(); +} + +// This read after reading ISR clears all error interrupts. We don't want compiler warnings, nor optimizations +#define UART_READ_RDR(uart) volatile uint8_t t = (uart)->RDR; UNUSED(t); + +static void uart_interrupt_handler(uart_ring *q) { + ENTER_CRITICAL(); + + // Read UART status. This is also the first step necessary in clearing most interrupts + uint32_t status = q->uart->ISR; + + // If RXFNE is set, perform a read. This clears RXFNE, ORE, IDLE, NF and FE + if((status & USART_ISR_RXNE_RXFNE) != 0U){ + uart_rx_ring(q); + } + + // Detect errors and clear them + uint32_t err = (status & USART_ISR_ORE) | (status & USART_ISR_NE) | (status & USART_ISR_FE) | (status & USART_ISR_PE); + if(err != 0U){ + #ifdef DEBUG_UART + print("Encountered UART error: "); puth(err); print("\n"); + #endif + UART_READ_RDR(q->uart) + } + + if ((err & USART_ISR_ORE) != 0U) { + q->uart->ICR |= USART_ICR_ORECF; + } else if ((err & USART_ISR_NE) != 0U) { + q->uart->ICR |= USART_ICR_NECF; + } else if ((err & USART_ISR_FE) != 0U) { + q->uart->ICR |= USART_ICR_FECF; + } else if ((err & USART_ISR_PE) != 0U) { + q->uart->ICR |= USART_ICR_PECF; + } else {} + + // Send if necessary + uart_tx_ring(q); + + EXIT_CRITICAL(); +} + +static void UART7_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_som_debug); } + +void uart_init(uart_ring *q, unsigned int baud) { + if (q->uart == UART7) { + REGISTER_INTERRUPT(UART7_IRQn, UART7_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_7) + + // UART7 is connected to APB1 at 60MHz + q->uart->BRR = 60000000U / baud; + q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; + + // Enable interrupt on RX not empty + q->uart->CR1 |= USART_CR1_RXNEIE; + + // Enable UART interrupts + NVIC_EnableIRQ(UART7_IRQn); + } +} diff --git a/board/stm32h7/lluart.h b/board/stm32h7/lluart.h index e18f1e9f6f3..159589aa5e2 100644 --- a/board/stm32h7/lluart.h +++ b/board/stm32h7/lluart.h @@ -1,102 +1,4 @@ -static void uart_rx_ring(uart_ring *q){ - // Do not read out directly if DMA enabled - ENTER_CRITICAL(); +#pragma once - // Read out RX buffer - uint8_t c = q->uart->RDR; // This read after reading SR clears a bunch of interrupts - - uint16_t next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; - - if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { - // overwrite mode: drop oldest byte - q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; - } - - // Do not overwrite buffer data - if (next_w_ptr != q->r_ptr_rx) { - q->elems_rx[q->w_ptr_rx] = c; - q->w_ptr_rx = next_w_ptr; - if (q->callback != NULL) { - q->callback(q); - } - } - - EXIT_CRITICAL(); -} - -void uart_tx_ring(uart_ring *q){ - ENTER_CRITICAL(); - // Send out next byte of TX buffer - if (q->w_ptr_tx != q->r_ptr_tx) { - // Only send if transmit register is empty (aka last byte has been sent) - if ((q->uart->ISR & USART_ISR_TXE_TXFNF) != 0U) { - q->uart->TDR = q->elems_tx[q->r_ptr_tx]; // This clears TXE - q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; - } - - // Enable TXE interrupt if there is still data to be sent - if(q->r_ptr_tx != q->w_ptr_tx){ - q->uart->CR1 |= USART_CR1_TXEIE; - } else { - q->uart->CR1 &= ~USART_CR1_TXEIE; - } - } - EXIT_CRITICAL(); -} - -// This read after reading ISR clears all error interrupts. We don't want compiler warnings, nor optimizations -#define UART_READ_RDR(uart) volatile uint8_t t = (uart)->RDR; UNUSED(t); - -static void uart_interrupt_handler(uart_ring *q) { - ENTER_CRITICAL(); - - // Read UART status. This is also the first step necessary in clearing most interrupts - uint32_t status = q->uart->ISR; - - // If RXFNE is set, perform a read. This clears RXFNE, ORE, IDLE, NF and FE - if((status & USART_ISR_RXNE_RXFNE) != 0U){ - uart_rx_ring(q); - } - - // Detect errors and clear them - uint32_t err = (status & USART_ISR_ORE) | (status & USART_ISR_NE) | (status & USART_ISR_FE) | (status & USART_ISR_PE); - if(err != 0U){ - #ifdef DEBUG_UART - print("Encountered UART error: "); puth(err); print("\n"); - #endif - UART_READ_RDR(q->uart) - } - - if ((err & USART_ISR_ORE) != 0U) { - q->uart->ICR |= USART_ICR_ORECF; - } else if ((err & USART_ISR_NE) != 0U) { - q->uart->ICR |= USART_ICR_NECF; - } else if ((err & USART_ISR_FE) != 0U) { - q->uart->ICR |= USART_ICR_FECF; - } else if ((err & USART_ISR_PE) != 0U) { - q->uart->ICR |= USART_ICR_PECF; - } else {} - - // Send if necessary - uart_tx_ring(q); - - EXIT_CRITICAL(); -} - -static void UART7_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_som_debug); } - -void uart_init(uart_ring *q, unsigned int baud) { - if (q->uart == UART7) { - REGISTER_INTERRUPT(UART7_IRQn, UART7_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_7) - - // UART7 is connected to APB1 at 60MHz - q->uart->BRR = 60000000U / baud; - q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; - - // Enable interrupt on RX not empty - q->uart->CR1 |= USART_CR1_RXNEIE; - - // Enable UART interrupts - NVIC_EnableIRQ(UART7_IRQn); - } -} +void uart_tx_ring(uart_ring *q); +void uart_init(uart_ring *q, unsigned int baud); diff --git a/board/stm32h7/llusb.c b/board/stm32h7/llusb.c new file mode 100644 index 00000000000..7612ee41b0c --- /dev/null +++ b/board/stm32h7/llusb.c @@ -0,0 +1,80 @@ +#include "board/config.h" +#include "board/stm32h7/llusb.h" + +USB_OTG_GlobalTypeDef *USBx = USB_OTG_HS; + +static void OTG_HS_IRQ_Handler(void) { + NVIC_DisableIRQ(OTG_HS_IRQn); + usb_irqhandler(); + NVIC_EnableIRQ(OTG_HS_IRQn); +} + +void usb_init(void) { + REGISTER_INTERRUPT(OTG_HS_IRQn, OTG_HS_IRQ_Handler, 1500000U, FAULT_INTERRUPT_RATE_USB) // TODO: Find out a better rate limit for USB. Now it's the 1.5MB/s rate + + // Disable global interrupt + USBx->GAHBCFG &= ~(USB_OTG_GAHBCFG_GINT); + // Select FS Embedded PHY + USBx->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL; + // Force device mode + USBx->GUSBCFG &= ~(USB_OTG_GUSBCFG_FHMOD | USB_OTG_GUSBCFG_FDMOD); + USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + delay(250000); // Wait for about 25ms (explicitly stated in H7 ref manual) + // Wait for AHB master IDLE state. + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U) {} + // Core Soft Reset + USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST) {} + // Activate the USB Transceiver + USBx->GCCFG |= USB_OTG_GCCFG_PWRDWN; + + for (uint8_t i = 0U; i < 15U; i++) { + USBx->DIEPTXF[i] = 0U; + } + + // VBUS Sensing setup + USBx_DEVICE->DCTL |= USB_OTG_DCTL_SDIS; + // Deactivate VBUS Sensing B + USBx->GCCFG &= ~(USB_OTG_GCCFG_VBDEN); + // B-peripheral session valid override enable + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN; + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; + // Restart the Phy Clock + USBx_PCGCCTL = 0U; + // Device mode configuration + USBx_DEVICE->DCFG |= DCFG_FRAME_INTERVAL_80; + USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK; + + // Flush FIFOs + USBx->GRSTCTL = (USB_OTG_GRSTCTL_TXFFLSH | (0x10U << 6)); + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH) {} + + USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH) {} + + // Clear all pending Device Interrupts + USBx_DEVICE->DIEPMSK = 0U; + USBx_DEVICE->DOEPMSK = 0U; + USBx_DEVICE->DAINTMSK = 0U; + USBx_DEVICE->DIEPMSK &= ~(USB_OTG_DIEPMSK_TXFURM); + + // Disable all interrupts. + USBx->GINTMSK = 0U; + // Clear any pending interrupts + USBx->GINTSTS = 0xBFFFFFFFU; + // Enable interrupts matching to the Device mode ONLY + USBx->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OTGINT | + USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_GONAKEFFM | USB_OTG_GINTMSK_GINAKEFFM | + USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | + USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM; + + // Set USB Turnaround time + USBx->GUSBCFG |= ((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT); + // Enables the controller's Global Int in the AHB Config reg + USBx->GAHBCFG |= USB_OTG_GAHBCFG_GINT; + // Soft disconnect disable: + USBx_DEVICE->DCTL &= ~(USB_OTG_DCTL_SDIS); + + // enable the IRQ + NVIC_EnableIRQ(OTG_HS_IRQn); +} diff --git a/board/stm32h7/llusb.h b/board/stm32h7/llusb.h index f1bcf16ad5e..a11d3f52c6b 100644 --- a/board/stm32h7/llusb.h +++ b/board/stm32h7/llusb.h @@ -1,79 +1,16 @@ -#include "llusb_declarations.h" +#pragma once -USB_OTG_GlobalTypeDef *USBx = USB_OTG_HS; +extern USB_OTG_GlobalTypeDef *USBx; -static void OTG_HS_IRQ_Handler(void) { - NVIC_DisableIRQ(OTG_HS_IRQn); - usb_irqhandler(); - NVIC_EnableIRQ(OTG_HS_IRQn); -} +#define USBx_DEVICE ((USB_OTG_DeviceTypeDef *)((uint32_t)USBx + USB_OTG_DEVICE_BASE)) +#define USBx_INEP(i) ((USB_OTG_INEndpointTypeDef *)((uint32_t)USBx + USB_OTG_IN_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) +#define USBx_OUTEP(i) ((USB_OTG_OUTEndpointTypeDef *)((uint32_t)USBx + USB_OTG_OUT_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) +#define USBx_DFIFO(i) *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_FIFO_BASE + ((i) * USB_OTG_FIFO_SIZE)) +#define USBx_PCGCCTL *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_PCGCCTL_BASE) -void usb_init(void) { - REGISTER_INTERRUPT(OTG_HS_IRQn, OTG_HS_IRQ_Handler, 1500000U, FAULT_INTERRUPT_RATE_USB) // TODO: Find out a better rate limit for USB. Now it's the 1.5MB/s rate +#define USBD_FS_TRDT_VALUE 6UL +#define USB_OTG_SPEED_FULL 3U +#define DCFG_FRAME_INTERVAL_80 0U - // Disable global interrupt - USBx->GAHBCFG &= ~(USB_OTG_GAHBCFG_GINT); - // Select FS Embedded PHY - USBx->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL; - // Force device mode - USBx->GUSBCFG &= ~(USB_OTG_GUSBCFG_FHMOD | USB_OTG_GUSBCFG_FDMOD); - USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; - delay(250000); // Wait for about 25ms (explicitly stated in H7 ref manual) - // Wait for AHB master IDLE state. - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U); - // Core Soft Reset - USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST); - // Activate the USB Transceiver - USBx->GCCFG |= USB_OTG_GCCFG_PWRDWN; - - for (uint8_t i = 0U; i < 15U; i++) { - USBx->DIEPTXF[i] = 0U; - } - - // VBUS Sensing setup - USBx_DEVICE->DCTL |= USB_OTG_DCTL_SDIS; - // Deactivate VBUS Sensing B - USBx->GCCFG &= ~(USB_OTG_GCCFG_VBDEN); - // B-peripheral session valid override enable - USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN; - USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; - // Restart the Phy Clock - USBx_PCGCCTL = 0U; - // Device mode configuration - USBx_DEVICE->DCFG |= DCFG_FRAME_INTERVAL_80; - USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK; - - // Flush FIFOs - USBx->GRSTCTL = (USB_OTG_GRSTCTL_TXFFLSH | (0x10U << 6)); - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH); - - USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; - while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH); - - // Clear all pending Device Interrupts - USBx_DEVICE->DIEPMSK = 0U; - USBx_DEVICE->DOEPMSK = 0U; - USBx_DEVICE->DAINTMSK = 0U; - USBx_DEVICE->DIEPMSK &= ~(USB_OTG_DIEPMSK_TXFURM); - - // Disable all interrupts. - USBx->GINTMSK = 0U; - // Clear any pending interrupts - USBx->GINTSTS = 0xBFFFFFFFU; - // Enable interrupts matching to the Device mode ONLY - USBx->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OTGINT | - USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_GONAKEFFM | USB_OTG_GINTMSK_GINAKEFFM | - USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | - USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM; - - // Set USB Turnaround time - USBx->GUSBCFG |= ((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT); - // Enables the controller's Global Int in the AHB Config reg - USBx->GAHBCFG |= USB_OTG_GAHBCFG_GINT; - // Soft disconnect disable: - USBx_DEVICE->DCTL &= ~(USB_OTG_DCTL_SDIS); - - // enable the IRQ - NVIC_EnableIRQ(OTG_HS_IRQn); -} +void usb_irqhandler(void); +void usb_init(void); diff --git a/board/stm32h7/llusb_declarations.h b/board/stm32h7/llusb_declarations.h deleted file mode 100644 index a11d3f52c6b..00000000000 --- a/board/stm32h7/llusb_declarations.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -extern USB_OTG_GlobalTypeDef *USBx; - -#define USBx_DEVICE ((USB_OTG_DeviceTypeDef *)((uint32_t)USBx + USB_OTG_DEVICE_BASE)) -#define USBx_INEP(i) ((USB_OTG_INEndpointTypeDef *)((uint32_t)USBx + USB_OTG_IN_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) -#define USBx_OUTEP(i) ((USB_OTG_OUTEndpointTypeDef *)((uint32_t)USBx + USB_OTG_OUT_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) -#define USBx_DFIFO(i) *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_FIFO_BASE + ((i) * USB_OTG_FIFO_SIZE)) -#define USBx_PCGCCTL *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_PCGCCTL_BASE) - -#define USBD_FS_TRDT_VALUE 6UL -#define USB_OTG_SPEED_FULL 3U -#define DCFG_FRAME_INTERVAL_80 0U - -void usb_irqhandler(void); -void usb_init(void); diff --git a/board/stm32h7/peripherals.c b/board/stm32h7/peripherals.c new file mode 100644 index 00000000000..a8ce701a272 --- /dev/null +++ b/board/stm32h7/peripherals.c @@ -0,0 +1,143 @@ +#include "board/config.h" +#include "board/stm32h7/peripherals.h" +#include "board/drivers/gpio.h" +#include "board/drivers/registers.h" + +void early_gpio_float(void) { + RCC->AHB4ENR = RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_GPIOFEN | RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_GPIOHEN; + GPIOA->MODER = 0xAB000000U; GPIOB->MODER = 0; GPIOC->MODER = 0; GPIOD->MODER = 0; GPIOE->MODER = 0; GPIOF->MODER = 0; GPIOG->MODER = 0; GPIOH->MODER = 0; + GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0; GPIOD->ODR = 0; GPIOE->ODR = 0; GPIOF->ODR = 0; GPIOG->ODR = 0; GPIOH->ODR = 0; + GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0; GPIOD->PUPDR = 0; GPIOE->PUPDR = 0; GPIOF->PUPDR = 0; GPIOG->PUPDR = 0; GPIOH->PUPDR = 0; +} + +void gpio_usb_init(void) { + // A11,A12: USB + set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG1_FS); + set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG1_FS); + GPIOA->OSPEEDR = GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12; +} + +void gpio_spi_init(void) { + set_gpio_alternate(GPIOE, 11, GPIO_AF5_SPI4); + set_gpio_alternate(GPIOE, 12, GPIO_AF5_SPI4); + set_gpio_alternate(GPIOE, 13, GPIO_AF5_SPI4); + set_gpio_alternate(GPIOE, 14, GPIO_AF5_SPI4); + register_set_bits(&(GPIOE->OSPEEDR), GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12 | GPIO_OSPEEDR_OSPEED13 | GPIO_OSPEEDR_OSPEED14); +} + +#ifdef BOOTSTUB +void gpio_usart2_init(void) { + // A2,A3: USART 2 for debugging + set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); + set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); +} +#endif + +void gpio_uart7_init(void) { + // E7,E8: UART 7 for debugging + set_gpio_alternate(GPIOE, 7, GPIO_AF7_UART7); + set_gpio_alternate(GPIOE, 8, GPIO_AF7_UART7); +} + +// Common GPIO initialization +void common_init_gpio(void) { + //F11: VOLT_S + set_gpio_pullup(GPIOF, 11, PULL_NONE); + set_gpio_mode(GPIOF, 11, MODE_ANALOG); + + gpio_usb_init(); + + // B8,B9: FDCAN1 + set_gpio_pullup(GPIOB, 8, PULL_NONE); + set_gpio_alternate(GPIOB, 8, GPIO_AF9_FDCAN1); + + set_gpio_pullup(GPIOB, 9, PULL_NONE); + set_gpio_alternate(GPIOB, 9, GPIO_AF9_FDCAN1); + + // B5,B6 (mplex to B12,B13): FDCAN2 + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_pullup(GPIOB, 13, PULL_NONE); + + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); + + // G9,G10: FDCAN3 + set_gpio_pullup(GPIOG, 9, PULL_NONE); + set_gpio_alternate(GPIOG, 9, GPIO_AF2_FDCAN3); + + set_gpio_pullup(GPIOG, 10, PULL_NONE); + set_gpio_alternate(GPIOG, 10, GPIO_AF2_FDCAN3); +} + +#ifdef BOOTSTUB +void flasher_peripherals_init(void) { + RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; + + // SPI + DMA + RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; + + // LED PWM + RCC->APB1LENR |= RCC_APB1LENR_TIM3EN; +} +#endif + +// Peripheral initialization +void peripherals_init(void) { + // enable GPIO(A,B,C,D,E,F,G,H) + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIODEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOEEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOFEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOGEN; + + // Enable CPU access to SRAMs for DMA + RCC->AHB2ENR |= RCC_AHB2ENR_SRAM1EN | RCC_AHB2ENR_SRAM2EN; + + // Supplemental + RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DAC DMA + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // SPI DMA + RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; + RCC->AHB4ENR |= RCC_AHB4ENR_BDMAEN; // Audio DMA + + // Connectivity + RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; // SPI + RCC->APB1LENR |= RCC_APB1LENR_I2C5EN; // codec I2C + RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; // USB + RCC->AHB1LPENR |= RCC_AHB1LPENR_USB1OTGHSLPEN; // USB LP needed for CSleep state(__WFI()) + RCC->AHB1LPENR &= ~(RCC_AHB1LPENR_USB1OTGHSULPILPEN); // disable USB ULPI + RCC->APB1LENR |= RCC_APB1LENR_UART7EN; // SOM uart + RCC->APB1HENR |= RCC_APB1HENR_FDCANEN; // FDCAN core enable + + // Analog + RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN; // Enable ADC12 clocks + RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; // Enable ADC3 clocks + RCC->APB1LENR |= RCC_APB1LENR_DAC12EN; // DAC + + // Audio + RCC->APB2ENR |= RCC_APB2ENR_DFSDM1EN; // D/S demodulator for mic + RCC->APB4ENR |= RCC_APB4ENR_SAI4EN; // SAI4 + + // Timers + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer + RCC->APB1LENR |= RCC_APB1LENR_TIM2EN; // main counter + 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 + RCC->APB1LENR |= RCC_APB1LENR_TIM12EN; // slow loop + RCC->APB1LENR |= RCC_APB1LENR_TIM5EN; // sound trigger timer + +#ifdef PANDA_JUNGLE + RCC->AHB3ENR |= RCC_AHB3ENR_SDMMC1EN; // SDMMC +#endif +} + +void enable_interrupt_timer(void) { + register_set_bits(&(RCC->APB1LENR), RCC_APB1LENR_TIM6EN); // Enable interrupt timer peripheral +} diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index 8eb384307f3..2df5a4bef8c 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -1,135 +1,14 @@ -#ifdef BOOTSTUB -void gpio_usb_init(void) { -#else -static void gpio_usb_init(void) { -#endif - // A11,A12: USB - set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG1_FS); - set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG1_FS); - GPIOA->OSPEEDR = GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12; -} +#pragma once -void gpio_spi_init(void) { - set_gpio_alternate(GPIOE, 11, GPIO_AF5_SPI4); - set_gpio_alternate(GPIOE, 12, GPIO_AF5_SPI4); - set_gpio_alternate(GPIOE, 13, GPIO_AF5_SPI4); - set_gpio_alternate(GPIOE, 14, GPIO_AF5_SPI4); - register_set_bits(&(GPIOE->OSPEEDR), GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12 | GPIO_OSPEEDR_OSPEED13 | GPIO_OSPEEDR_OSPEED14); -} +void early_gpio_float(void); +void gpio_usb_init(void); +void gpio_spi_init(void); +void gpio_uart7_init(void); +void common_init_gpio(void); +void peripherals_init(void); +void enable_interrupt_timer(void); #ifdef BOOTSTUB -void gpio_usart2_init(void) { - // A2,A3: USART 2 for debugging - set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); - set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); -} +void gpio_usart2_init(void); +void flasher_peripherals_init(void); #endif - -void gpio_uart7_init(void) { - // E7,E8: UART 7 for debugging - set_gpio_alternate(GPIOE, 7, GPIO_AF7_UART7); - set_gpio_alternate(GPIOE, 8, GPIO_AF7_UART7); -} - -// Common GPIO initialization -void common_init_gpio(void) { - //F11: VOLT_S - set_gpio_pullup(GPIOF, 11, PULL_NONE); - set_gpio_mode(GPIOF, 11, MODE_ANALOG); - - gpio_usb_init(); - - // B8,B9: FDCAN1 - set_gpio_pullup(GPIOB, 8, PULL_NONE); - set_gpio_alternate(GPIOB, 8, GPIO_AF9_FDCAN1); - - set_gpio_pullup(GPIOB, 9, PULL_NONE); - set_gpio_alternate(GPIOB, 9, GPIO_AF9_FDCAN1); - - // B5,B6 (mplex to B12,B13): FDCAN2 - set_gpio_pullup(GPIOB, 12, PULL_NONE); - set_gpio_pullup(GPIOB, 13, PULL_NONE); - - set_gpio_pullup(GPIOB, 5, PULL_NONE); - set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); - - set_gpio_pullup(GPIOB, 6, PULL_NONE); - set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); - - // G9,G10: FDCAN3 - set_gpio_pullup(GPIOG, 9, PULL_NONE); - set_gpio_alternate(GPIOG, 9, GPIO_AF2_FDCAN3); - - set_gpio_pullup(GPIOG, 10, PULL_NONE); - set_gpio_alternate(GPIOG, 10, GPIO_AF2_FDCAN3); -} - -#ifdef BOOTSTUB -void flasher_peripherals_init(void) { - RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; - - // SPI + DMA - RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; - RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; - - // LED PWM - RCC->APB1LENR |= RCC_APB1LENR_TIM3EN; -} -#endif - -// Peripheral initialization -void peripherals_init(void) { - // enable GPIO(A,B,C,D,E,F,G,H) - RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; - RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN; - RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN; - RCC->AHB4ENR |= RCC_AHB4ENR_GPIODEN; - RCC->AHB4ENR |= RCC_AHB4ENR_GPIOEEN; - RCC->AHB4ENR |= RCC_AHB4ENR_GPIOFEN; - RCC->AHB4ENR |= RCC_AHB4ENR_GPIOGEN; - - // Enable CPU access to SRAMs for DMA - RCC->AHB2ENR |= RCC_AHB2ENR_SRAM1EN | RCC_AHB2ENR_SRAM2EN; - - // Supplemental - RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DAC DMA - RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // SPI DMA - RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; - RCC->AHB4ENR |= RCC_AHB4ENR_BDMAEN; // Audio DMA - - // Connectivity - RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; // SPI - RCC->APB1LENR |= RCC_APB1LENR_I2C5EN; // codec I2C - RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; // USB - RCC->AHB1LPENR |= RCC_AHB1LPENR_USB1OTGHSLPEN; // USB LP needed for CSleep state(__WFI()) - RCC->AHB1LPENR &= ~(RCC_AHB1LPENR_USB1OTGHSULPILPEN); // disable USB ULPI - RCC->APB1LENR |= RCC_APB1LENR_UART7EN; // SOM uart - RCC->APB1HENR |= RCC_APB1HENR_FDCANEN; // FDCAN core enable - - // Analog - RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN; // Enable ADC12 clocks - RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; // Enable ADC3 clocks - RCC->APB1LENR |= RCC_APB1LENR_DAC12EN; // DAC - - // Audio - RCC->APB2ENR |= RCC_APB2ENR_DFSDM1EN; // D/S demodulator for mic - RCC->APB4ENR |= RCC_APB4ENR_SAI4EN; // SAI4 - - // Timers - RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer - RCC->APB1LENR |= RCC_APB1LENR_TIM2EN; // main counter - 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 - RCC->APB1LENR |= RCC_APB1LENR_TIM12EN; // slow loop - RCC->APB1LENR |= RCC_APB1LENR_TIM5EN; // sound trigger timer - -#ifdef PANDA_JUNGLE - RCC->AHB3ENR |= RCC_AHB3ENR_SDMMC1EN; // SDMMC -#endif -} - -void enable_interrupt_timer(void) { - register_set_bits(&(RCC->APB1LENR), RCC_APB1LENR_TIM6EN); // Enable interrupt timer peripheral -} diff --git a/board/stm32h7/sound.c b/board/stm32h7/sound.c new file mode 100644 index 00000000000..76b82ba0f55 --- /dev/null +++ b/board/stm32h7/sound.c @@ -0,0 +1,225 @@ +#include "board/config.h" +#include "board/stm32h7/sound.h" + +__attribute__((section(".sram4"))) static uint16_t sound_rx_buf[2][SOUND_RX_BUF_SIZE]; +__attribute__((section(".sram4"))) static uint16_t sound_tx_buf[2][SOUND_TX_BUF_SIZE]; +__attribute__((section(".sram4"))) static uint32_t mic_rx_buf[2][MIC_RX_BUF_SIZE]; +__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) { + sound_idle_count--; + if (sound_idle_count == 0U) { + current_board->set_amp_enabled(false); + register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + sound_output_level = 0U; + } + } + + if (mic_idle_count > 0U) { + mic_idle_count--; + if (mic_idle_count == 0U) { + register_clear_bits(&DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN); + mic_buffer_count = 0U; + } + } +} + +// Recording processing +static void DMA1_Stream0_IRQ_Handler(void) { + DMA1->LIFCR |= 0x7DU; // clear flags + + uint8_t tx_buf_idx = (((BDMA_Channel1->CCR & BDMA_CCR_CT) >> BDMA_CCR_CT_Pos) == 1U) ? 0U : 1U; + + if (mic_buffer_count < MIC_SKIP_BUFFERS) { + // Send silence during settling + mic_buffer_count++; + for (uint16_t i = 0U; i < MIC_TX_BUF_SIZE; i++) { + mic_tx_buf[tx_buf_idx][i] = 0U; + } + } else { + // process samples + uint8_t buf_idx = (((DMA1_Stream0->CR & DMA_SxCR_CT) >> DMA_SxCR_CT_Pos) == 1U) ? 0U : 1U; + for (uint16_t i=0U; i < MIC_RX_BUF_SIZE; i++) { + mic_tx_buf[tx_buf_idx][2U*i] = ((mic_rx_buf[buf_idx][i] >> 16U) & 0xFFFFU); + mic_tx_buf[tx_buf_idx][(2U*i)+1U] = mic_tx_buf[tx_buf_idx][2U*i]; + } + } +} + +// Playback processing +static void BDMA_Channel0_IRQ_Handler(void) { + static uint8_t playback_buf = 0U; + + BDMA->IFCR |= BDMA_IFCR_CGIF0; // clear flag + + uint8_t rx_buf_idx = (((BDMA_Channel0->CCR & BDMA_CCR_CT) >> BDMA_CCR_CT_Pos) == 1U) ? 0U : 1U; + playback_buf = 1U - playback_buf; + + // wait until we're done playing the current buf + for (uint32_t timeout_counter = 100000U; timeout_counter > 0U; timeout_counter--){ + uint8_t playing_buf = (DMA1_Stream1->CR & DMA_SxCR_CT) >> DMA_SxCR_CT_Pos; + if ((playing_buf != playback_buf) || ((DMA1_Stream1->CR & DMA_SxCR_EN) == 0U)) { + break; + } + } + + // 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 + 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) { + current_board->set_amp_enabled(true); + + // empty the other buf and start playing that + for (uint16_t i=0U; i < SOUND_TX_BUF_SIZE; i++) { + sound_tx_buf[1U - playback_buf][i] = (1UL << 11); + } + register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + DMA1_Stream1->CR = (DMA1_Stream1->CR & ~DMA_SxCR_CT_Msk) | ((1UL - playback_buf) << DMA_SxCR_CT_Pos); + register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } + sound_idle_count = SOUND_IDLE_TIMEOUT; + } + + // manage mic state + if (mic_idle_count == 0U) { + register_set_bits(&DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN); + DFSDM1_Filter0->FLTCR1 |= DFSDM_FLTCR1_RSWSTART; + } + mic_idle_count = SOUND_IDLE_TIMEOUT; +} + +void sound_init_dac(void) { + // Init DAC + DAC1->DHR12R1 = (1UL << 11); + DAC1->DHR12R2 = (1UL << 11); + register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); + register_set(&DAC1->CR, DAC_CR_TEN1 | (4U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); + register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); + + // Setup DMAMUX (DAC_CH1_DMA as input) + register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); + + // Setup DMA + register_set(&DMA1_Stream1->PAR, (uint32_t) &(DAC1->DHR12R1), 0xFFFFFFFFU); + register_set(&DMA1_Stream1->M0AR, (uint32_t) sound_tx_buf[0], 0xFFFFFFFFU); + register_set(&DMA1_Stream1->M1AR, (uint32_t) sound_tx_buf[1], 0xFFFFFFFFU); + register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); + DMA1_Stream1->NDTR = SOUND_TX_BUF_SIZE; + DMA1_Stream1->CR = DMA_SxCR_DBM | (0b11UL << DMA_SxCR_PL_Pos) | (0b01UL << DMA_SxCR_MSIZE_Pos) | (0b01UL << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | (1U << DMA_SxCR_DIR_Pos); +} + +void sound_stop_dac(void) { + register_clear_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); + BDMA->IFCR = 0xFFFFFFFFU; + + register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + while ((DMA1_Stream1->CR & DMA_SxCR_EN) != 0U) {} + DMA1->LIFCR = (0x3FU << 6); + + register_clear_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); + while ((DAC1->CR & (DAC_CR_EN1 | DAC_CR_EN2)) != 0U) {} +} + +void sound_init(void) { + REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA) + REGISTER_INTERRUPT(DMA1_Stream0_IRQn, DMA1_Stream0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA) + + // Init DAC and its DMA + sound_init_dac(); + + // Init trigger timer (little slower than 48kHz, pulled in sync by SAI4_FS_B) + register_set(&TIM5->PSC, 2600U, 0xFFFFU); + register_set(&TIM5->ARR, 100U, 0xFFFFFFFFU); + register_set(&TIM5->AF1, (0b0010UL << TIM5_AF1_ETRSEL_Pos), TIM5_AF1_ETRSEL_Msk); + register_set(&TIM5->CR2, (0b010U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); + register_set(&TIM5->SMCR, TIM_SMCR_ECE | (0b00111UL << TIM_SMCR_TS_Pos)| (0b0100UL << TIM_SMCR_SMS_Pos), 0x31FFF7U); + TIM5->CNT = 0U; TIM5->SR = 0U; + TIM5->CR1 |= TIM_CR1_CEN; + + // sync both SAIs + register_set(&SAI4->GCR, (0b10UL << SAI_GCR_SYNCOUT_Pos), SAI_GCR_SYNCIN_Msk | SAI_GCR_SYNCOUT_Msk); + + // stereo audio in + register_set(&SAI4_Block_B->CR1, SAI_xCR1_DMAEN | (0b00UL << SAI_xCR1_SYNCEN_Pos) | (0b100U << SAI_xCR1_DS_Pos) | (0b11U << SAI_xCR1_MODE_Pos), 0x0FFB3FEFU); + register_set(&SAI4_Block_B->CR2, (0b001U << SAI_xCR2_FTH_Pos), 0xFFFBU); + register_set(&SAI4_Block_B->FRCR, (31U << SAI_xFRCR_FRL_Pos), 0x7FFFFU); + register_set(&SAI4_Block_B->SLOTR, (0b11UL << SAI_xSLOTR_SLOTEN_Pos) | (1UL << SAI_xSLOTR_NBSLOT_Pos) | (0b01UL << SAI_xSLOTR_SLOTSZ_Pos), 0xFFFF0FDFU); // NBSLOT definition is vague + + // init sound DMA (SAI4_B -> memory, double buffers) + register_set(&BDMA_Channel0->CPAR, (uint32_t) &(SAI4_Block_B->DR), 0xFFFFFFFFU); + register_set(&BDMA_Channel0->CM0AR, (uint32_t) sound_rx_buf[0], 0xFFFFFFFFU); + register_set(&BDMA_Channel0->CM1AR, (uint32_t) sound_rx_buf[1], 0xFFFFFFFFU); + BDMA_Channel0->CNDTR = SOUND_RX_BUF_SIZE; + register_set(&BDMA_Channel0->CCR, BDMA_CCR_DBM | (0b01UL << BDMA_CCR_MSIZE_Pos) | (0b01UL << BDMA_CCR_PSIZE_Pos) | BDMA_CCR_MINC | BDMA_CCR_CIRC | BDMA_CCR_TCIE, 0xFFFFU); + register_set(&DMAMUX2_Channel0->CCR, 16U, DMAMUX_CxCR_DMAREQ_ID_Msk); // SAI4_B_DMA + register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); + + // mic output + register_set(&SAI4_Block_A->CR1, SAI_xCR1_DMAEN | (0b01UL << SAI_xCR1_SYNCEN_Pos) | (0b100UL << SAI_xCR1_DS_Pos) | (0b10UL << SAI_xCR1_MODE_Pos), 0x0FFB3FEFU); + register_set(&SAI4_Block_A->CR2, 0U, 0xFFFBU); + register_set(&SAI4_Block_A->FRCR, (31U << SAI_xFRCR_FRL_Pos), 0x7FFFFU); + register_set(&SAI4_Block_A->SLOTR, (0b11UL << SAI_xSLOTR_SLOTEN_Pos) | (1UL << SAI_xSLOTR_NBSLOT_Pos) | (0b01U << SAI_xSLOTR_SLOTSZ_Pos), 0xFFFF0FDFU); // NBSLOT definition is vague + + // init DFSDM for PDM mic + register_set(&DFSDM1_Channel0->CHCFGR1, (76UL << DFSDM_CHCFGR1_CKOUTDIV_Pos) | DFSDM_CHCFGR1_CHEN, 0xC0FFF1EFU); // CH0 controls the clock + register_set(&DFSDM1_Channel3->CHCFGR1, (0b01UL << DFSDM_CHCFGR1_SPICKSEL_Pos) | (0b00U << DFSDM_CHCFGR1_SITP_Pos) | DFSDM_CHCFGR1_CHEN, 0x0000F1EFU); // SITP determines sample edge + register_set(&DFSDM1_Filter0->FLTFCR, (0U << DFSDM_FLTFCR_IOSR_Pos) | (64UL << DFSDM_FLTFCR_FOSR_Pos) | (4UL << DFSDM_FLTFCR_FORD_Pos), 0xE3FF00FFU); + register_set(&DFSDM1_Filter0->FLTCR1, DFSDM_FLTCR1_FAST | (3UL << DFSDM_FLTCR1_RCH_Pos) | DFSDM_FLTCR1_RDMAEN | DFSDM_FLTCR1_RCONT | DFSDM_FLTCR1_DFEN, 0x672E7F3BU); + + // DMA (DFSDM1 -> memory) + register_set(&DMA1_Stream0->PAR, (uint32_t) &DFSDM1_Filter0->FLTRDATAR, 0xFFFFFFFFU); + register_set(&DMA1_Stream0->M0AR, (uint32_t)mic_rx_buf[0], 0xFFFFFFFFU); + register_set(&DMA1_Stream0->M1AR, (uint32_t)mic_rx_buf[1], 0xFFFFFFFFU); + DMA1_Stream0->NDTR = MIC_RX_BUF_SIZE; + register_set(&DMA1_Stream0->CR, DMA_SxCR_DBM | (0b10UL << DMA_SxCR_MSIZE_Pos) | (0b10UL << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_TCIE, 0x01F7FFFFU); + register_set(&DMAMUX1_Channel0->CCR, 101U, DMAMUX_CxCR_DMAREQ_ID_Msk); // DFSDM1_DMA0 + register_set_bits(&DMA1_Stream0->CR, DMA_SxCR_EN); + DMA1->LIFCR |= 0x7DU; // clear flags + + // DMA (memory -> SAI4) + register_set(&BDMA_Channel1->CPAR, (uint32_t) &(SAI4_Block_A->DR), 0xFFFFFFFFU); + register_set(&BDMA_Channel1->CM0AR, (uint32_t) mic_tx_buf[0], 0xFFFFFFFFU); + register_set(&BDMA_Channel1->CM1AR, (uint32_t) mic_tx_buf[1], 0xFFFFFFFFU); + BDMA_Channel1->CNDTR = MIC_TX_BUF_SIZE; + register_set(&BDMA_Channel1->CCR, BDMA_CCR_DBM | (0b01UL << BDMA_CCR_MSIZE_Pos) |(0b01UL << BDMA_CCR_PSIZE_Pos) | BDMA_CCR_MINC | BDMA_CCR_CIRC | (0b1U << BDMA_CCR_DIR_Pos), 0xFFFFU); + register_set(&DMAMUX2_Channel1->CCR, 15U, DMAMUX_CxCR_DMAREQ_ID_Msk); // SAI4_A_DMA + register_set_bits(&BDMA_Channel1->CCR, BDMA_CCR_EN); + + // enable all initted blocks + register_set_bits(&SAI4_Block_A->CR1, SAI_xCR1_SAIEN); + register_set_bits(&SAI4_Block_B->CR1, SAI_xCR1_SAIEN); + NVIC_EnableIRQ(BDMA_Channel0_IRQn); + NVIC_EnableIRQ(DMA1_Stream0_IRQn); +} diff --git a/board/stm32h7/sound.h b/board/stm32h7/sound.h index 80c9317fda9..e0e02fa22f6 100644 --- a/board/stm32h7/sound.h +++ b/board/stm32h7/sound.h @@ -1,228 +1,16 @@ +#pragma once + #define SOUND_RX_BUF_SIZE 1000U #define SOUND_TX_BUF_SIZE (SOUND_RX_BUF_SIZE/2U) #define MIC_RX_BUF_SIZE 512U #define MIC_TX_BUF_SIZE (MIC_RX_BUF_SIZE * 2U) -__attribute__((section(".sram4"))) static uint16_t sound_rx_buf[2][SOUND_RX_BUF_SIZE]; -__attribute__((section(".sram4"))) static uint16_t sound_tx_buf[2][SOUND_TX_BUF_SIZE]; -__attribute__((section(".sram4"))) static uint32_t mic_rx_buf[2][MIC_RX_BUF_SIZE]; -__attribute__((section(".sram4"))) static uint16_t mic_tx_buf[2][MIC_TX_BUF_SIZE]; #define SOUND_IDLE_TIMEOUT 4U #define MIC_SKIP_BUFFERS 2U // Skip first 2 buffers (1024 samples = ~21ms at 48kHz) -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) { - sound_idle_count--; - if (sound_idle_count == 0U) { - current_board->set_amp_enabled(false); - register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - sound_output_level = 0U; - } - } - - if (mic_idle_count > 0U) { - mic_idle_count--; - if (mic_idle_count == 0U) { - register_clear_bits(&DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN); - mic_buffer_count = 0U; - } - } -} - -// Recording processing -static void DMA1_Stream0_IRQ_Handler(void) { - DMA1->LIFCR |= 0x7DU; // clear flags - - uint8_t tx_buf_idx = (((BDMA_Channel1->CCR & BDMA_CCR_CT) >> BDMA_CCR_CT_Pos) == 1U) ? 0U : 1U; - - if (mic_buffer_count < MIC_SKIP_BUFFERS) { - // Send silence during settling - mic_buffer_count++; - for (uint16_t i = 0U; i < MIC_TX_BUF_SIZE; i++) { - mic_tx_buf[tx_buf_idx][i] = 0U; - } - } else { - // process samples - uint8_t buf_idx = (((DMA1_Stream0->CR & DMA_SxCR_CT) >> DMA_SxCR_CT_Pos) == 1U) ? 0U : 1U; - for (uint16_t i=0U; i < MIC_RX_BUF_SIZE; i++) { - mic_tx_buf[tx_buf_idx][2U*i] = ((mic_rx_buf[buf_idx][i] >> 16U) & 0xFFFFU); - mic_tx_buf[tx_buf_idx][(2U*i)+1U] = mic_tx_buf[tx_buf_idx][2U*i]; - } - } -} - -// Playback processing -static void BDMA_Channel0_IRQ_Handler(void) { - static uint8_t playback_buf = 0U; - - BDMA->IFCR |= BDMA_IFCR_CGIF0; // clear flag - - uint8_t rx_buf_idx = (((BDMA_Channel0->CCR & BDMA_CCR_CT) >> BDMA_CCR_CT_Pos) == 1U) ? 0U : 1U; - playback_buf = 1U - playback_buf; - - // wait until we're done playing the current buf - for (uint32_t timeout_counter = 100000U; timeout_counter > 0U; timeout_counter--){ - uint8_t playing_buf = (DMA1_Stream1->CR & DMA_SxCR_CT) >> DMA_SxCR_CT_Pos; - if ((playing_buf != playback_buf) || ((DMA1_Stream1->CR & DMA_SxCR_EN) == 0U)) { - break; - } - } - - // 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 - 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) { - current_board->set_amp_enabled(true); - - // empty the other buf and start playing that - for (uint16_t i=0U; i < SOUND_TX_BUF_SIZE; i++) { - sound_tx_buf[1U - playback_buf][i] = (1UL << 11); - } - register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - DMA1_Stream1->CR = (DMA1_Stream1->CR & ~DMA_SxCR_CT_Msk) | ((1UL - playback_buf) << DMA_SxCR_CT_Pos); - register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - } - sound_idle_count = SOUND_IDLE_TIMEOUT; - } - - // manage mic state - if (mic_idle_count == 0U) { - register_set_bits(&DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN); - DFSDM1_Filter0->FLTCR1 |= DFSDM_FLTCR1_RSWSTART; - } - mic_idle_count = SOUND_IDLE_TIMEOUT; -} - -void sound_init_dac(void) { - // Init DAC - DAC1->DHR12R1 = (1UL << 11); - DAC1->DHR12R2 = (1UL << 11); - register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); - register_set(&DAC1->CR, DAC_CR_TEN1 | (4U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); - register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); - - // Setup DMAMUX (DAC_CH1_DMA as input) - register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); - - // Setup DMA - register_set(&DMA1_Stream1->PAR, (uint32_t) &(DAC1->DHR12R1), 0xFFFFFFFFU); - register_set(&DMA1_Stream1->M0AR, (uint32_t) sound_tx_buf[0], 0xFFFFFFFFU); - register_set(&DMA1_Stream1->M1AR, (uint32_t) sound_tx_buf[1], 0xFFFFFFFFU); - register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); - DMA1_Stream1->NDTR = SOUND_TX_BUF_SIZE; - DMA1_Stream1->CR = DMA_SxCR_DBM | (0b11UL << DMA_SxCR_PL_Pos) | (0b01UL << DMA_SxCR_MSIZE_Pos) | (0b01UL << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | (1U << DMA_SxCR_DIR_Pos); -} - -static void sound_stop_dac(void) { - register_clear_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); - BDMA->IFCR = 0xFFFFFFFFU; - - register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); - while ((DMA1_Stream1->CR & DMA_SxCR_EN) != 0U) {} - DMA1->LIFCR = (0x3FU << 6); - - register_clear_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2); - while ((DAC1->CR & (DAC_CR_EN1 | DAC_CR_EN2)) != 0U) {} -} - -void sound_init(void) { - REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA) - REGISTER_INTERRUPT(DMA1_Stream0_IRQn, DMA1_Stream0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA) - - // Init DAC and its DMA - sound_init_dac(); - - // Init trigger timer (little slower than 48kHz, pulled in sync by SAI4_FS_B) - register_set(&TIM5->PSC, 2600U, 0xFFFFU); - register_set(&TIM5->ARR, 100U, 0xFFFFFFFFU); - register_set(&TIM5->AF1, (0b0010UL << TIM5_AF1_ETRSEL_Pos), TIM5_AF1_ETRSEL_Msk); - register_set(&TIM5->CR2, (0b010U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); - register_set(&TIM5->SMCR, TIM_SMCR_ECE | (0b00111UL << TIM_SMCR_TS_Pos)| (0b0100UL << TIM_SMCR_SMS_Pos), 0x31FFF7U); - TIM5->CNT = 0U; TIM5->SR = 0U; - TIM5->CR1 |= TIM_CR1_CEN; - - // sync both SAIs - register_set(&SAI4->GCR, (0b10UL << SAI_GCR_SYNCOUT_Pos), SAI_GCR_SYNCIN_Msk | SAI_GCR_SYNCOUT_Msk); - - // stereo audio in - register_set(&SAI4_Block_B->CR1, SAI_xCR1_DMAEN | (0b00UL << SAI_xCR1_SYNCEN_Pos) | (0b100U << SAI_xCR1_DS_Pos) | (0b11U << SAI_xCR1_MODE_Pos), 0x0FFB3FEFU); - register_set(&SAI4_Block_B->CR2, (0b001U << SAI_xCR2_FTH_Pos), 0xFFFBU); - register_set(&SAI4_Block_B->FRCR, (31U << SAI_xFRCR_FRL_Pos), 0x7FFFFU); - register_set(&SAI4_Block_B->SLOTR, (0b11UL << SAI_xSLOTR_SLOTEN_Pos) | (1UL << SAI_xSLOTR_NBSLOT_Pos) | (0b01UL << SAI_xSLOTR_SLOTSZ_Pos), 0xFFFF0FDFU); // NBSLOT definition is vague - - // init sound DMA (SAI4_B -> memory, double buffers) - register_set(&BDMA_Channel0->CPAR, (uint32_t) &(SAI4_Block_B->DR), 0xFFFFFFFFU); - register_set(&BDMA_Channel0->CM0AR, (uint32_t) sound_rx_buf[0], 0xFFFFFFFFU); - register_set(&BDMA_Channel0->CM1AR, (uint32_t) sound_rx_buf[1], 0xFFFFFFFFU); - BDMA_Channel0->CNDTR = SOUND_RX_BUF_SIZE; - register_set(&BDMA_Channel0->CCR, BDMA_CCR_DBM | (0b01UL << BDMA_CCR_MSIZE_Pos) | (0b01UL << BDMA_CCR_PSIZE_Pos) | BDMA_CCR_MINC | BDMA_CCR_CIRC | BDMA_CCR_TCIE, 0xFFFFU); - register_set(&DMAMUX2_Channel0->CCR, 16U, DMAMUX_CxCR_DMAREQ_ID_Msk); // SAI4_B_DMA - register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); - - // mic output - register_set(&SAI4_Block_A->CR1, SAI_xCR1_DMAEN | (0b01UL << SAI_xCR1_SYNCEN_Pos) | (0b100UL << SAI_xCR1_DS_Pos) | (0b10UL << SAI_xCR1_MODE_Pos), 0x0FFB3FEFU); - register_set(&SAI4_Block_A->CR2, 0U, 0xFFFBU); - register_set(&SAI4_Block_A->FRCR, (31U << SAI_xFRCR_FRL_Pos), 0x7FFFFU); - register_set(&SAI4_Block_A->SLOTR, (0b11UL << SAI_xSLOTR_SLOTEN_Pos) | (1UL << SAI_xSLOTR_NBSLOT_Pos) | (0b01U << SAI_xSLOTR_SLOTSZ_Pos), 0xFFFF0FDFU); // NBSLOT definition is vague - - // init DFSDM for PDM mic - register_set(&DFSDM1_Channel0->CHCFGR1, (90UL << DFSDM_CHCFGR1_CKOUTDIV_Pos) | DFSDM_CHCFGR1_CHEN, 0xC0FFF1EFU); // CH0 controls the clock - register_set(&DFSDM1_Channel3->CHCFGR1, (0b01UL << DFSDM_CHCFGR1_SPICKSEL_Pos) | (0b00U << DFSDM_CHCFGR1_SITP_Pos) | DFSDM_CHCFGR1_CHEN, 0x0000F1EFU); // SITP determines sample edge - register_set(&DFSDM1_Filter0->FLTFCR, (0U << DFSDM_FLTFCR_IOSR_Pos) | (54UL << DFSDM_FLTFCR_FOSR_Pos) | (4UL << DFSDM_FLTFCR_FORD_Pos), 0xE3FF00FFU); - register_set(&DFSDM1_Filter0->FLTCR1, DFSDM_FLTCR1_FAST | (3UL << DFSDM_FLTCR1_RCH_Pos) | DFSDM_FLTCR1_RDMAEN | DFSDM_FLTCR1_RCONT | DFSDM_FLTCR1_DFEN, 0x672E7F3BU); - - // DMA (DFSDM1 -> memory) - register_set(&DMA1_Stream0->PAR, (uint32_t) &DFSDM1_Filter0->FLTRDATAR, 0xFFFFFFFFU); - register_set(&DMA1_Stream0->M0AR, (uint32_t)mic_rx_buf[0], 0xFFFFFFFFU); - register_set(&DMA1_Stream0->M1AR, (uint32_t)mic_rx_buf[1], 0xFFFFFFFFU); - DMA1_Stream0->NDTR = MIC_RX_BUF_SIZE; - register_set(&DMA1_Stream0->CR, DMA_SxCR_DBM | (0b10UL << DMA_SxCR_MSIZE_Pos) | (0b10UL << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_TCIE, 0x01F7FFFFU); - register_set(&DMAMUX1_Channel0->CCR, 101U, DMAMUX_CxCR_DMAREQ_ID_Msk); // DFSDM1_DMA0 - register_set_bits(&DMA1_Stream0->CR, DMA_SxCR_EN); - DMA1->LIFCR |= 0x7DU; // clear flags - // DMA (memory -> SAI4) - register_set(&BDMA_Channel1->CPAR, (uint32_t) &(SAI4_Block_A->DR), 0xFFFFFFFFU); - register_set(&BDMA_Channel1->CM0AR, (uint32_t) mic_tx_buf[0], 0xFFFFFFFFU); - register_set(&BDMA_Channel1->CM1AR, (uint32_t) mic_tx_buf[1], 0xFFFFFFFFU); - BDMA_Channel1->CNDTR = MIC_TX_BUF_SIZE; - register_set(&BDMA_Channel1->CCR, BDMA_CCR_DBM | (0b01UL << BDMA_CCR_MSIZE_Pos) |(0b01UL << BDMA_CCR_PSIZE_Pos) | BDMA_CCR_MINC | BDMA_CCR_CIRC | (0b1U << BDMA_CCR_DIR_Pos), 0xFFFFU); - register_set(&DMAMUX2_Channel1->CCR, 15U, DMAMUX_CxCR_DMAREQ_ID_Msk); // SAI4_A_DMA - register_set_bits(&BDMA_Channel1->CCR, BDMA_CCR_EN); +void sound_tick(void); +void sound_init_dac(void); +void sound_stop_dac(void); +void sound_init(void); - // enable all initted blocks - register_set_bits(&SAI4_Block_A->CR1, SAI_xCR1_SAIEN); - register_set_bits(&SAI4_Block_B->CR1, SAI_xCR1_SAIEN); - NVIC_EnableIRQ(BDMA_Channel0_IRQn); - NVIC_EnableIRQ(DMA1_Stream0_IRQn); -} +extern uint16_t sound_output_level; diff --git a/board/stm32h7/stm32h7_config.h b/board/stm32h7/stm32h7_config.h index 6fd6eae2d38..edfc2efb5df 100644 --- a/board/stm32h7/stm32h7_config.h +++ b/board/stm32h7/stm32h7_config.h @@ -44,12 +44,12 @@ separate IRQs for RX and TX. #define DEVICE_SERIAL_NUMBER_ADDRESS 0x080FFFC0U #include "board/can.h" -#include "board/comms_definitions.h" +#include "board/comms.h" #ifndef BOOTSTUB - #include "board/main_definitions.h" + #include "board/main_globals.h" #else - #include "board/bootstub_declarations.h" + #include "board/bootstub_globals.h" #endif #include "board/libc.h" @@ -57,45 +57,48 @@ separate IRQs for RX and TX. #include "board/sys/faults.h" #include "board/utils.h" +#ifdef PANDA_JUNGLE +#include "board/jungle/boards/board_declarations.h" +#elif defined(PANDA_BODY) +#include "board/body/boards/board_declarations.h" +#else +#include "board/boards/board_declarations.h" +#endif + +#include "board/health.h" +#include "board/crc.h" +#include "board/drivers/led.h" +#include "board/drivers/pwm.h" +#include "board/stm32h7/lladc.h" +#include "board/drivers/can_common.h" +#include "board/drivers/fdcan.h" +#include "board/drivers/harness.h" +#include "board/drivers/bootkick.h" +#include "board/drivers/clock_source.h" +#include "board/drivers/fan.h" +#include "board/drivers/simple_watchdog.h" +#include "board/drivers/usb.h" +#include "board/stm32h7/sound.h" +#include "board/drivers/fake_siren.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" #include "board/drivers/timers.h" -#if !defined(BOOTSTUB) - #include "board/drivers/uart.h" - #include "board/stm32h7/lluart.h" -#endif - -#ifdef PANDA_JUNGLE -#include "board/jungle/stm32h7/board.h" -#elif defined(PANDA_BODY) -#include "board/body/stm32h7/board.h" -#else -#include "board/stm32h7/board.h" -#endif -#include "board/stm32h7/clock.h" +#include "board/drivers/uart.h" -#ifdef BOOTSTUB - #include "board/stm32h7/llflash.h" -#else - #include "board/stm32h7/llfdcan.h" -#endif +#include "board/drivers/spi.h" +#include "board/stm32h7/llfdcan.h" #include "board/stm32h7/llusb.h" - -#include "board/drivers/spi.h" #include "board/stm32h7/llspi.h" +#include "board/stm32h7/lluart.h" +#include "board/stm32h7/llflash.h" +#include "board/stm32h7/llfan.h" +#include "board/stm32h7/lli2c.h" -void early_gpio_float(void) { - RCC->AHB4ENR = RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_GPIOFEN | RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_GPIOHEN; - GPIOA->MODER = 0xAB000000U; GPIOB->MODER = 0; GPIOC->MODER = 0; GPIOD->MODER = 0; GPIOE->MODER = 0; GPIOF->MODER = 0; GPIOG->MODER = 0; GPIOH->MODER = 0; - GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0; GPIOD->ODR = 0; GPIOE->ODR = 0; GPIOF->ODR = 0; GPIOG->ODR = 0; GPIOH->ODR = 0; - GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0; GPIOD->PUPDR = 0; GPIOE->PUPDR = 0; GPIOF->PUPDR = 0; GPIOG->PUPDR = 0; GPIOH->PUPDR = 0; -} +#include "board/stm32h7/peripherals.h" +#include "board/stm32h7/interrupt_handlers.h" +#include "board/stm32h7/clock.h" diff --git a/board/sys/critical.c b/board/sys/critical.c new file mode 100644 index 00000000000..ae93461ae92 --- /dev/null +++ b/board/sys/critical.c @@ -0,0 +1,26 @@ +#ifdef STM32H7 +#include "stm32h7xx.h" +#else +// Stubs for native builds +// cppcheck-suppress misra-c2012-21.1 ; CMSIS-compatible stubs for non-STM32 builds +#define __enable_irq() +// cppcheck-suppress misra-c2012-21.1 +#define __disable_irq() +#endif + +#include "board/sys/sys.h" +#include "board/sys/critical.h" + +// ********************* Critical section helpers ********************* +uint8_t global_critical_depth = 0U; +volatile bool interrupts_enabled = false; + +void enable_interrupts(void) { + interrupts_enabled = true; + __enable_irq(); +} + +void disable_interrupts(void) { + interrupts_enabled = false; + __disable_irq(); +} diff --git a/board/sys/critical.h b/board/sys/critical.h index 70c143f7012..459f021f129 100644 --- a/board/sys/critical.h +++ b/board/sys/critical.h @@ -1,16 +1,3 @@ -#include "board/sys/sys.h" - -// ********************* Critical section helpers ********************* -uint8_t global_critical_depth = 0U; - -static volatile bool interrupts_enabled = false; +#pragma once -void enable_interrupts(void) { - interrupts_enabled = true; - __enable_irq(); -} - -void disable_interrupts(void) { - interrupts_enabled = false; - __disable_irq(); -} +#include "board/sys/sys.h" diff --git a/board/sys/faults.c b/board/sys/faults.c new file mode 100644 index 00000000000..91723766f47 --- /dev/null +++ b/board/sys/faults.c @@ -0,0 +1,27 @@ +#include "board/sys/sys.h" +#include "board/sys/faults.h" +#include "board/libc.h" + +uint8_t fault_status = FAULT_STATUS_NONE; +uint32_t faults = 0U; + +void fault_occurred(uint32_t fault) { + if ((faults & fault) == 0U) { + if ((PERMANENT_FAULTS & fault) != 0U) { + print("Permanent fault occurred: 0x"); puth(fault); print("\n"); + fault_status = FAULT_STATUS_PERMANENT; + } else { + print("Temporary fault occurred: 0x"); puth(fault); print("\n"); + fault_status = FAULT_STATUS_TEMPORARY; + } + } + faults |= fault; +} + +void fault_recovered(uint32_t fault) { + if ((PERMANENT_FAULTS & fault) == 0U) { + faults &= ~fault; + } else { + print("Cannot recover from a permanent fault!\n"); + } +} diff --git a/board/sys/faults.h b/board/sys/faults.h index 005457988b9..459f021f129 100644 --- a/board/sys/faults.h +++ b/board/sys/faults.h @@ -1,25 +1,3 @@ -#include "board/sys/sys.h" - -uint8_t fault_status = FAULT_STATUS_NONE; -uint32_t faults = 0U; +#pragma once -void fault_occurred(uint32_t fault) { - if ((faults & fault) == 0U) { - if ((PERMANENT_FAULTS & fault) != 0U) { - print("Permanent fault occurred: 0x"); puth(fault); print("\n"); - fault_status = FAULT_STATUS_PERMANENT; - } else { - print("Temporary fault occurred: 0x"); puth(fault); print("\n"); - fault_status = FAULT_STATUS_TEMPORARY; - } - } - faults |= fault; -} - -void fault_recovered(uint32_t fault) { - if ((PERMANENT_FAULTS & fault) == 0U) { - faults &= ~fault; - } else { - print("Cannot recover from a permanent fault!\n"); - } -} +#include "board/sys/sys.h" diff --git a/board/sys/power_saving.c b/board/sys/power_saving.c new file mode 100644 index 00000000000..05fda30a5bc --- /dev/null +++ b/board/sys/power_saving.c @@ -0,0 +1,150 @@ +#include "board/config.h" +#include "board/sys/power_saving.h" +#include "board/drivers/gpio.h" +#include "board/drivers/registers.h" +#include "board/drivers/harness.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; + } +} + +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(); +} diff --git a/board/sys/power_saving.h b/board/sys/power_saving.h index accfdb01f3a..d9cdf1c4483 100644 --- a/board/sys/power_saving.h +++ b/board/sys/power_saving.h @@ -1,148 +1,12 @@ -#include "board/sys/sys.h" +#pragma once -// 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 +#include -// 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; +extern bool power_save_enabled; #ifdef ALLOW_DEBUG -volatile bool stop_mode_requested = false; +extern volatile bool stop_mode_requested; #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(); -} +void enable_can_transceivers(bool enabled); +void set_power_save_state(bool enable); +void enter_stop_mode(void); diff --git a/board/sys/sys.h b/board/sys/sys.h index fe5527a0c72..92b53a7e524 100644 --- a/board/sys/sys.h +++ b/board/sys/sys.h @@ -1,11 +1,15 @@ #pragma once +#include +#include + // ******************** critical ******************** void enable_interrupts(void); void disable_interrupts(void); extern uint8_t global_critical_depth; +extern volatile bool interrupts_enabled; #ifndef ENTER_CRITICAL #define ENTER_CRITICAL() \ @@ -58,6 +62,4 @@ void fault_recovered(uint32_t fault); // ******************** power_saving ******************** -extern bool power_save_enabled; - -void set_power_save_state(bool enable); +#include "board/sys/power_saving.h" diff --git a/board/utils.h b/board/utils.h index f355ce8c2f2..20b24621411 100644 --- a/board/utils.h +++ b/board/utils.h @@ -42,6 +42,6 @@ // compute the time elapsed (in microseconds) from 2 counter samples // case where ts < ts_last is ok: overflow is properly re-casted into uint32_t -uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last) { +static inline uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last) { return ts - ts_last; } diff --git a/tests/libpanda/SConscript b/tests/libpanda/SConscript index 1b82b0e450f..7eb6209db52 100644 --- a/tests/libpanda/SConscript +++ b/tests/libpanda/SConscript @@ -7,9 +7,18 @@ env = Environment( '-std=gnu11', '-Wfatal-errors', '-Wno-pointer-to-int-cast', + '-include', 'fake_stm.h', ], CPPPATH=[".", "../../", "../../board/", opendbc.INCLUDE_PATH], ) panda = env.SharedObject("panda.os", "panda.c") -libpanda = env.SharedLibrary("libpanda.so", [panda]) +libc = env.SharedObject("libc.os", "../../board/libc.c") +crc = env.SharedObject("crc.os", "../../board/crc.c") +provision = env.SharedObject("provision.os", "../../board/provision.c") +critical = env.SharedObject("critical.os", "../../board/sys/critical.c") +faults = env.SharedObject("faults.os", "../../board/sys/faults.c") +main_globals = env.SharedObject("main_globals.os", "../../board/main_globals.c") +can_common = env.SharedObject("can_common.os", "../../board/drivers/can_common.c") +can_comms = env.SharedObject("can_comms.os", "../../board/can_comms.c") +libpanda = env.SharedLibrary("libpanda.so", [panda, libc, crc, provision, critical, faults, main_globals, can_common, can_comms]) diff --git a/tests/libpanda/panda.c b/tests/libpanda/panda.c index 5c82a7b7d4e..7c2bf5ea71b 100644 --- a/tests/libpanda/panda.c +++ b/tests/libpanda/panda.c @@ -1,13 +1,25 @@ #include "fake_stm.h" #include "config.h" #include "can.h" +#include "drivers/registers.h" +#include "drivers/simple_watchdog.h" +#include "drivers/led.h" +#include "drivers/gpio.h" +#include "drivers/pwm.h" +#include "drivers/timers.h" +#include "drivers/bootkick.h" +#include "drivers/clock_source.h" +#include "drivers/fan.h" +#include "drivers/harness.h" +#include "drivers/spi.h" +#include "drivers/usb.h" +#include "drivers/can_common.h" bool can_init(uint8_t can_number) { return true; } void process_can(uint8_t can_number) { } //int safety_tx_hook(CANPacket_t *to_send) { return 1; } typedef struct harness_configuration harness_configuration; -void refresh_can_tx_slots_available(void); void can_tx_comms_resume_usb(void) { }; void can_tx_comms_resume_spi(void) { }; @@ -16,13 +28,12 @@ void can_tx_comms_resume_spi(void) { }; #include "libc.h" #include "boards/board_declarations.h" #include "opendbc/safety/safety.h" -#include "main_definitions.h" -#include "drivers/can_common.h" +#include "main_globals.h" can_ring *rx_q = &can_rx_q; can_ring *tx1_q = &can_tx1_q; can_ring *tx2_q = &can_tx2_q; can_ring *tx3_q = &can_tx3_q; -#include "comms_definitions.h" +#include "comms.h" #include "can_comms.h" diff --git a/tests/misra/checkers.txt b/tests/misra/checkers.txt index 44e6aa13f34..d4fa9d49041 100644 --- a/tests/misra/checkers.txt +++ b/tests/misra/checkers.txt @@ -5,7 +5,7 @@ Cppcheck checkers list from test_misra.sh: TEST variant options: ---enable=all --disable=unusedFunction --addon=misra -DSTM32H7 -DSTM32H725xx -I /board/stm32h7/inc/ /board/main.c +--enable=all --disable=unusedFunction --addon=misra -DSTM32H7 -DSTM32H725xx -I /board/stm32h7/inc/ /board/main.c -DDRIVER_BUILD /board/stm32h7/interrupt_handlers.c /board/stm32h7/peripherals.c /board/stm32h7/clock.c /board/stm32h7/llfdcan.c /board/stm32h7/llusb.c /board/stm32h7/llspi.c /board/stm32h7/lluart.c /board/stm32h7/lladc.c /board/stm32h7/board.c /board/stm32h7/llfan.c /board/stm32h7/lli2c.c /board/stm32h7/sound.c /board/stm32h7/llflash.c /board/drivers/gpio.c /board/drivers/registers.c /board/drivers/interrupts.c /board/drivers/timers.c /board/drivers/pwm.c /board/drivers/led.c /board/drivers/can_common.c /board/drivers/fdcan.c /board/drivers/uart.c /board/drivers/spi.c /board/drivers/usb.c /board/drivers/simple_watchdog.c /board/drivers/bootkick.c /board/drivers/clock_source.c /board/drivers/fan.c /board/drivers/harness.c /board/drivers/fake_siren.c /board/can_comms.c /board/sys/power_saving.c /board/sys/critical.c /board/sys/faults.c /board/main_comms.c /board/libc.c /board/crc.c /board/provision.c /board/main_globals.c /board/early_init.c /board/boards/unused_funcs.c /board/boards/red.c /board/boards/tres.c /board/boards/cuatro.c Critical errors diff --git a/tests/misra/suppressions.txt b/tests/misra/suppressions.txt index 4800a270bcb..a4f0626c584 100644 --- a/tests/misra/suppressions.txt +++ b/tests/misra/suppressions.txt @@ -12,10 +12,21 @@ misra-c2012-20.10 # needed since not all of these suppressions are applicable to all builds unmatchedSuppression +# static arrays in external opendbc headers included across TUs +misra-c2012-5.9:*opendbc/* + # All interrupt handlers are defined, including ones we don't use unusedFunction:*/interrupt_handlers*.h +unusedFunction:*/interrupt_handlers*.c # all of the below suppressions are from new checks introduced after updating -# cppcheck from 2.5 -> 2.13. they are listed here to separate the update from +# cppcheck from 2.5 -> 2.13 -> 2.16. they are listed here to separate the update from # fixing the violations and all are intended to be removed soon after +misra-c2012-2.3 # type aliases used across TUs but cppcheck single-file analysis can't see usage +misra-c2012-2.4 # struct tags used across TUs but cppcheck single-file analysis can't see usage misra-c2012-2.5 # unused macros. a few legit, rest aren't common between F4/H7 builds. should we do this in the unusedFunction pass? +misra-c2012-8.5 # multi-TU: extern declarations in shared headers seen by multiple translation units +misra-c2012-8.6 # multi-TU: external definitions seen across translation units in single cppcheck run +misra-c2012-8.7 # functions need external linkage for multi-TU builds +misra-c2012-20.1 # conditional includes after code are structurally needed +misra-c2012-21.2 # intentional bare-metal libc reimplementation diff --git a/tests/misra/test_misra.sh b/tests/misra/test_misra.sh index ba38568075c..2b2edb482e0 100755 --- a/tests/misra/test_misra.sh +++ b/tests/misra/test_misra.sh @@ -61,8 +61,55 @@ cppcheck() { PANDA_OPTS="--enable=all --disable=unusedFunction --addon=misra" +PANDA_H7_DRIVER_SOURCES="-DDRIVER_BUILD \ + $PANDA_DIR/board/stm32h7/interrupt_handlers.c \ + $PANDA_DIR/board/stm32h7/peripherals.c \ + $PANDA_DIR/board/stm32h7/clock.c \ + $PANDA_DIR/board/stm32h7/llfdcan.c \ + $PANDA_DIR/board/stm32h7/llusb.c \ + $PANDA_DIR/board/stm32h7/llspi.c \ + $PANDA_DIR/board/stm32h7/lluart.c \ + $PANDA_DIR/board/stm32h7/lladc.c \ + $PANDA_DIR/board/stm32h7/board.c \ + $PANDA_DIR/board/stm32h7/llfan.c \ + $PANDA_DIR/board/stm32h7/lli2c.c \ + $PANDA_DIR/board/stm32h7/sound.c \ + $PANDA_DIR/board/stm32h7/llflash.c \ + $PANDA_DIR/board/drivers/gpio.c \ + $PANDA_DIR/board/drivers/registers.c \ + $PANDA_DIR/board/drivers/interrupts.c \ + $PANDA_DIR/board/drivers/timers.c \ + $PANDA_DIR/board/drivers/pwm.c \ + $PANDA_DIR/board/drivers/led.c \ + $PANDA_DIR/board/drivers/can_common.c \ + $PANDA_DIR/board/drivers/fdcan.c \ + $PANDA_DIR/board/drivers/uart.c \ + $PANDA_DIR/board/drivers/spi.c \ + $PANDA_DIR/board/drivers/usb.c \ + $PANDA_DIR/board/drivers/simple_watchdog.c \ + $PANDA_DIR/board/drivers/bootkick.c \ + $PANDA_DIR/board/drivers/clock_source.c \ + $PANDA_DIR/board/drivers/fan.c \ + $PANDA_DIR/board/drivers/harness.c \ + $PANDA_DIR/board/drivers/fake_siren.c \ + $PANDA_DIR/board/can_comms.c \ + $PANDA_DIR/board/sys/power_saving.c \ + $PANDA_DIR/board/sys/critical.c \ + $PANDA_DIR/board/sys/faults.c \ + $PANDA_DIR/board/main_comms.c \ + $PANDA_DIR/board/libc.c \ + $PANDA_DIR/board/crc.c \ + $PANDA_DIR/board/provision.c \ + $PANDA_DIR/board/main_globals.c \ + $PANDA_DIR/board/early_init.c \ + $PANDA_DIR/board/boards/unused_funcs.c \ + $PANDA_DIR/board/boards/red.c \ + $PANDA_DIR/board/boards/tres.c \ + $PANDA_DIR/board/boards/cuatro.c" + printf "\n${GREEN}** PANDA H7 CODE **${NC}\n" -cppcheck $PANDA_OPTS -DSTM32H7 -DSTM32H725xx -I $PANDA_DIR/board/stm32h7/inc/ $PANDA_DIR/board/main.c +cppcheck $PANDA_OPTS -DSTM32H7 -DSTM32H725xx -I $PANDA_DIR/board/stm32h7/inc/ \ + $PANDA_DIR/board/main.c $PANDA_H7_DRIVER_SOURCES # unused needs to run globally #printf "\n${GREEN}** UNUSED ALL CODE **${NC}\n" diff --git a/tests/misra/test_mutation.py b/tests/misra/test_mutation.py index 1f25f5ff1a5..effd691339a 100755 --- a/tests/misra/test_mutation.py +++ b/tests/misra/test_mutation.py @@ -20,14 +20,16 @@ # bootstub only files 'board/flasher.h', + 'board/flasher.c', 'board/bootstub.c', - 'board/bootstub_declarations.h', + 'board/bootstub_globals.h', + 'board/bootstub_globals.c', 'board/stm32h7/llflash.h', ) mutations = [ (None, None, False), # no mods, should pass - ("board/stm32h7/llfdcan.h", "s/return ret;/if (true) { return ret; } else { return false; }/g", True), + ("board/utils.h", "$a void test(int tmp) { if (true && tmp++) {;} }", True), ] patterns = [