From 00295b2dc3f80605fbadca9871085610c46734f4 Mon Sep 17 00:00:00 2001 From: Rishab Date: Thu, 29 Oct 2026 20:16:10 -0400 Subject: [PATCH 1/9] added clock configs --- firmware/boards/TOAD_G4/variant_TOAD_G4.cpp | 50 +- firmware/src/prog_main.cpp | 478 ++++++++++---------- 2 files changed, 282 insertions(+), 246 deletions(-) diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp index 9efc9ce..5b0efea 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp @@ -104,30 +104,31 @@ const uint32_t analogInputPin[] = { * @retval None */ WEAK void SystemClock_Config(void) { - RCC_OscInitTypeDef RCC_OscInitStruct = {}; - RCC_ClkInitTypeDef RCC_ClkInitStruct = {}; - RCC_PeriphCLKInitTypeDef PeriphClkInit = {}; + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_CRSInitTypeDef pInit = {0}; /** Configure the main internal regulator output voltage */ - HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST); + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); + /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_HSI48; - RCC_OscInitStruct.HSIState = RCC_HSI_ON; - RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 | RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; - RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4; - RCC_OscInitStruct.PLL.PLLN = 85; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2; + RCC_OscInitStruct.PLL.PLLN = 60; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; - RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV4; + RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } + /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; @@ -136,14 +137,37 @@ WEAK void SystemClock_Config(void) { RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) { Error_Handler(); } - /** Initializes the peripherals clocks + + /** Enable the SYSCFG APB clock + */ + __HAL_RCC_CRS_CLK_ENABLE(); + + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + + /** Configures CRS */ + pInit.Prescaler = RCC_CRS_SYNC_DIV1; + pInit.Source = RCC_CRS_SYNC_SOURCE_USB; + pInit.Polarity = RCC_CRS_SYNC_POLARITY_RISING; + pInit.ReloadValue = __HAL_RCC_CRS_RELOADVALUE_CALCULATE(48000000, 1000); + pInit.ErrorLimitValue = 34; + pInit.HSI48CalibrationValue = 32; + + HAL_RCCEx_CRSConfig(&pInit); + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB; PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_HSI48; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } + + /* Peripheral clock enable */ + __HAL_RCC_USB_CLK_ENABLE(); + + /* Peripheral interrupt init */ + HAL_NVIC_SetPriority(USB_LP_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(USB_LP_IRQn); } diff --git a/firmware/src/prog_main.cpp b/firmware/src/prog_main.cpp index a3e479e..02103d2 100644 --- a/firmware/src/prog_main.cpp +++ b/firmware/src/prog_main.cpp @@ -1,239 +1,251 @@ +// #include + +// #include "CommsSerial.h" +// #include "prog_pins.h" +// #include "toad_can_bus.h" + +// // Based on +// // https://www.st.com/resource/en/application_note/an4286-how-to-use-spi-protocol-in-bootloader-on-stm32-mcus-stmicroelectronics.pdf +// // Citations commented as [pg#] + +// CommsSerial_t USB_CommsSerial; +// CommsSerial_t HW_CommsSerial(PIN_HW_COMM_SERIAL_RX, PIN_HW_COMM_SERIAL_TX); +// CommsSerial_t HW_FallbackSerial(PIN_HW_FALLBACK_SERIAL_RX, PIN_HW_FALLBACK_SERIAL_TX); + +// SPIClass Prog_SPI(PIN_PROG_SPI_MOSI, PIN_PROG_SPI_MISO, PIN_PROG_SPI_SCK); + +// constexpr uint8_t ACK = 0x79; +// constexpr uint8_t NACK = 0x15; +// constexpr uint8_t SYNC = 0x5A; +// constexpr uint8_t CMD_WriteMem = 0x31; +// constexpr uint8_t CMD_EraseMem = 0x44; + +// // If return value of function is false, return false from the outer function +// // TODO - not sure if this is the design pattern we want +// #define ExitOnFail(expr) \ +// do { \ +// if (!(expr)) \ +// return false; \ +// } while (0) + +// prog_id_t prog_type; + +// enum prog_state_t { STATE_IDLE, STATE_PRE_ERASE, STATE_READY, STATE_PAGE_SELECTED } prog_state; + +// // 2 MB of Flash divided into 512 x 4096 byte virtual pages. +// // Chunks for the active page are received 32 bytes at a time, for 128 chunks per page. +// // The active page is written out 256 bytes at a time. +// constexpr size_t CAN_CHUNK_SIZE = 32; // bytes +// constexpr size_t WRITE_CHUNK_SIZE = 256; // bytes; +// constexpr size_t PAGE_CACHE_SIZE = 4096; // bytes +// constexpr size_t NUM_CAN_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / CAN_CHUNK_SIZE; +// constexpr size_t NUM_WRITE_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / WRITE_CHUNK_SIZE; +// constexpr size_t NUM_PAGE_CACHES = 512; +// static_assert(PAGE_CACHE_SIZE * NUM_PAGE_CACHES == 2097152); + +// uint32_t active_pg_addr; +// uint8_t page_cache[PAGE_CACHE_SIZE]; +// bool chunk_rcv[NUM_CAN_CHUNKS_PER_PAGE]; + +// void setup() { +// // All shared interfaces are begun here. + +// // Use same baud rate on all Comm Serials for consistency. +// USB_CommsSerial.begin(RADIO_BAUD); +// HW_CommsSerial.begin(RADIO_BAUD); +// HW_FallbackSerial.begin(RADIO_BAUD); + +// // Configure SPI interface and set CS HIGH +// Prog_SPI.begin(); +// digitalWrite(PIN_PROG_SPI_CS, HIGH); + +// // Configure BOOT and NRST to default the STM32H7 into normal code execution +// digitalWrite(PIN_H7_BOOT, BOOT_MODE_RUN); +// pinMode(PIN_H7_BOOT, OUTPUT); + +// digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); +// pinMode(PIN_H7_NRST, OUTPUT); + +// pinMode(PIN_PROG_ID, INPUT); +// prog_type = (digitalRead(PIN_PROG_ID) == PROG_ID_FLIGHT_CONTROLLER) ? PROG_FLIGHT_CONTROLLER : PROG_ENGINE_CONTROLLER; + +// delay(3000); + +// if (prog_type == PROG_FLIGHT_CONTROLLER) { +// CommsSerial.println("Flight Controller Programmer Started!"); +// } else { +// CommsSerial.println("Engine Controller Programmer Started!"); +// } + +// prog_state = STATE_IDLE; +// } + +// void reset_h7() { +// digitalWrite(PIN_H7_NRST, NRST_MODE_RST); +// delay(1000); +// digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); +// } + +// // Sends ACK frame, returns true if bootloader +// // responded with ACK, false otherwise +// // TODO - pretty sure sometimes this has to cycle around until ACK +// bool spi_ack_frame() { +// Prog_SPI.transfer(0x00); +// uint8_t resp = Prog_SPI.transfer(0x00); +// Prog_SPI.transfer(ACK); + +// if (resp == ACK) { +// return true; +// } else if (resp == NACK) { +// return false; +// } else { +// return false; +// } +// } + +// bool enter_bootloader() { +// // Reset the H7 into the bootloader +// digitalWrite(PIN_H7_BOOT, BOOT_MODE_FLASH); +// reset_h7(); + +// // Send SYNC byte and get ACK [6] +// digitalWrite(PIN_PROG_SPI_CS, LOW); + +// Prog_SPI.transfer(SYNC); +// ExitOnFail(spi_ack_frame()); + +// return true; +// } + +// bool erase_memory() { +// Prog_SPI.transfer(SYNC); +// Prog_SPI.transfer(CMD_EraseMem); +// Prog_SPI.transfer(~CMD_EraseMem); +// ExitOnFail(spi_ack_frame()); + +// // Mass erase [26] +// Prog_SPI.transfer(0xFF); +// Prog_SPI.transfer(0xFF); +// Prog_SPI.transfer(0xFF ^ 0xFF); +// ExitOnFail(spi_ack_frame()); + +// return true; +// } + +// bool write_memory(uint32_t addr, const uint8_t *bytes, size_t len) { +// Prog_SPI.transfer(SYNC); +// Prog_SPI.transfer(CMD_WriteMem); +// Prog_SPI.transfer(~CMD_WriteMem); +// ExitOnFail(spi_ack_frame()); + +// uint8_t addr_byte_0 = (addr >> 24) & 0xFF; +// uint8_t addr_byte_1 = (addr >> 16) & 0xFF; +// uint8_t addr_byte_2 = (addr >> 8) & 0xFF; +// uint8_t addr_byte_3 = (addr >> 0) & 0xFF; + +// Prog_SPI.transfer(addr_byte_0); +// Prog_SPI.transfer(addr_byte_1); +// Prog_SPI.transfer(addr_byte_2); +// Prog_SPI.transfer(addr_byte_3); +// Prog_SPI.transfer(addr_byte_0 ^ addr_byte_1 ^ addr_byte_2 ^ addr_byte_3); +// ExitOnFail(spi_ack_frame()); + +// uint8_t chk_sum = (len - 1) & 0xFF; +// Prog_SPI.transfer((len - 1) & 0xFF); +// for (size_t i = 0; i < len; i++) { +// chk_sum ^= bytes[i]; +// Prog_SPI.transfer(bytes[i]); +// } +// Prog_SPI.transfer(chk_sum); +// ExitOnFail(spi_ack_frame()); + +// return true; +// } + +// void loop() { +// // TODO - prog state machine +// // Wait for CAN cmds, control reset and boot +// // Load flash files and flash over SPI + +// // can.pump_events() or similar +// // process commands - note that each command must be guaranteed to not halt +// // TODO - figure out how to parse the incoming cmd and payload +// uint8_t *raw_bytes; +// size_t raw_msg_len; + +// CAN_Msg_Decoder raw_msg(raw_bytes, raw_msg_len, prog_state); + +// if (const auto msg = raw_msg.decode()) { +// // TODO - this + +// } else if (const auto msg = raw_msg.decode()) { +// // TODO - enforce state +// reset_h7(); + +// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_IDLE)) { +// if (enter_bootloader()) { +// prog_state = STATE_PRE_ERASE; +// } else { +// // TODO - handle error +// } + +// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PRE_ERASE)) { +// if (erase_memory()) { +// prog_state = STATE_READY; +// } else { +// // TODO - handle error +// } + +// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_READY)) { +// prog_state = STATE_PAGE_SELECTED; +// active_pg_addr = msg->page_addr; +// for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { +// chunk_rcv[i] = false; +// } + +// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { +// if (msg->page_addr == active_pg_addr) { +// static_assert(sizeof(msg->flash_bytes) == CAN_CHUNK_SIZE); +// memcpy(&page_cache[msg->chunk_addr * CAN_CHUNK_SIZE], msg->flash_bytes, CAN_CHUNK_SIZE); +// chunk_rcv[msg->chunk_addr] = true; +// } else { +// // TODO - handle wrong page addr +// } + +// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { +// bool all_chunk_rcv = true; +// for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { +// if (!chunk_rcv) { +// can_msg_request_mem_packet_t resp; +// resp.chunk_addr = i; +// resp.page_addr = active_pg_addr; +// // TODO - send request message +// all_chunk_rcv = false; +// } +// } + +// if (all_chunk_rcv) { +// for (size_t i = 0; i < NUM_WRITE_CHUNKS_PER_PAGE; i++) { +// static_assert(WRITE_CHUNK_SIZE <= 256); +// write_memory(active_pg_addr * PAGE_CACHE_SIZE + WRITE_CHUNK_SIZE * i, &page_cache[WRITE_CHUNK_SIZE * i], +// WRITE_CHUNK_SIZE); +// // TODO - send ok +// } +// prog_state = STATE_READY; +// } +// } + +// raw_msg.send_error_if_not_decoded(); +// } + +// #include "CommsSerial.h" #include -#include "CommsSerial.h" -#include "prog_pins.h" -#include "toad_can_bus.h" - -// Based on -// https://www.st.com/resource/en/application_note/an4286-how-to-use-spi-protocol-in-bootloader-on-stm32-mcus-stmicroelectronics.pdf -// Citations commented as [pg#] - -CommsSerial_t USB_CommsSerial; -CommsSerial_t HW_CommsSerial(PIN_HW_COMM_SERIAL_RX, PIN_HW_COMM_SERIAL_TX); -CommsSerial_t HW_FallbackSerial(PIN_HW_FALLBACK_SERIAL_RX, PIN_HW_FALLBACK_SERIAL_TX); - -SPIClass Prog_SPI(PIN_PROG_SPI_MOSI, PIN_PROG_SPI_MISO, PIN_PROG_SPI_SCK); - -constexpr uint8_t ACK = 0x79; -constexpr uint8_t NACK = 0x15; -constexpr uint8_t SYNC = 0x5A; -constexpr uint8_t CMD_WriteMem = 0x31; -constexpr uint8_t CMD_EraseMem = 0x44; - -// If return value of function is false, return false from the outer function -// TODO - not sure if this is the design pattern we want -#define ExitOnFail(expr) \ - do { \ - if (!(expr)) \ - return false; \ - } while (0) - -prog_id_t prog_type; - -enum prog_state_t { STATE_IDLE, STATE_PRE_ERASE, STATE_READY, STATE_PAGE_SELECTED } prog_state; - -// 2 MB of Flash divided into 512 x 4096 byte virtual pages. -// Chunks for the active page are received 32 bytes at a time, for 128 chunks per page. -// The active page is written out 256 bytes at a time. -constexpr size_t CAN_CHUNK_SIZE = 32; // bytes -constexpr size_t WRITE_CHUNK_SIZE = 256; // bytes; -constexpr size_t PAGE_CACHE_SIZE = 4096; // bytes -constexpr size_t NUM_CAN_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / CAN_CHUNK_SIZE; -constexpr size_t NUM_WRITE_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / WRITE_CHUNK_SIZE; -constexpr size_t NUM_PAGE_CACHES = 512; -static_assert(PAGE_CACHE_SIZE * NUM_PAGE_CACHES == 2097152); - -uint32_t active_pg_addr; -uint8_t page_cache[PAGE_CACHE_SIZE]; -bool chunk_rcv[NUM_CAN_CHUNKS_PER_PAGE]; - void setup() { - // All shared interfaces are begun here. - - // Use same baud rate on all Comm Serials for consistency. - USB_CommsSerial.begin(RADIO_BAUD); - HW_CommsSerial.begin(RADIO_BAUD); - HW_FallbackSerial.begin(RADIO_BAUD); - - // Configure SPI interface and set CS HIGH - Prog_SPI.begin(); - digitalWrite(PIN_PROG_SPI_CS, HIGH); - - // Configure BOOT and NRST to default the STM32H7 into normal code execution - digitalWrite(PIN_H7_BOOT, BOOT_MODE_RUN); - pinMode(PIN_H7_BOOT, OUTPUT); - - digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); - pinMode(PIN_H7_NRST, OUTPUT); - - pinMode(PIN_PROG_ID, INPUT); - prog_type = (digitalRead(PIN_PROG_ID) == PROG_ID_FLIGHT_CONTROLLER) ? PROG_FLIGHT_CONTROLLER : PROG_ENGINE_CONTROLLER; - - delay(3000); - - if (prog_type == PROG_FLIGHT_CONTROLLER) { - CommsSerial.println("Flight Controller Programmer Started!"); - } else { - CommsSerial.println("Engine Controller Programmer Started!"); - } - - prog_state = STATE_IDLE; -} - -void reset_h7() { - digitalWrite(PIN_H7_NRST, NRST_MODE_RST); - delay(1000); - digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); -} - -// Sends ACK frame, returns true if bootloader -// responded with ACK, false otherwise -// TODO - pretty sure sometimes this has to cycle around until ACK -bool spi_ack_frame() { - Prog_SPI.transfer(0x00); - uint8_t resp = Prog_SPI.transfer(0x00); - Prog_SPI.transfer(ACK); - - if (resp == ACK) { - return true; - } else if (resp == NACK) { - return false; - } else { - return false; - } -} - -bool enter_bootloader() { - // Reset the H7 into the bootloader - digitalWrite(PIN_H7_BOOT, BOOT_MODE_FLASH); - reset_h7(); - - // Send SYNC byte and get ACK [6] - digitalWrite(PIN_PROG_SPI_CS, LOW); - - Prog_SPI.transfer(SYNC); - ExitOnFail(spi_ack_frame()); - - return true; -} - -bool erase_memory() { - Prog_SPI.transfer(SYNC); - Prog_SPI.transfer(CMD_EraseMem); - Prog_SPI.transfer(~CMD_EraseMem); - ExitOnFail(spi_ack_frame()); - - // Mass erase [26] - Prog_SPI.transfer(0xFF); - Prog_SPI.transfer(0xFF); - Prog_SPI.transfer(0xFF ^ 0xFF); - ExitOnFail(spi_ack_frame()); - - return true; -} + Serial.begin(9600); -bool write_memory(uint32_t addr, const uint8_t *bytes, size_t len) { - Prog_SPI.transfer(SYNC); - Prog_SPI.transfer(CMD_WriteMem); - Prog_SPI.transfer(~CMD_WriteMem); - ExitOnFail(spi_ack_frame()); - - uint8_t addr_byte_0 = (addr >> 24) & 0xFF; - uint8_t addr_byte_1 = (addr >> 16) & 0xFF; - uint8_t addr_byte_2 = (addr >> 8) & 0xFF; - uint8_t addr_byte_3 = (addr >> 0) & 0xFF; - - Prog_SPI.transfer(addr_byte_0); - Prog_SPI.transfer(addr_byte_1); - Prog_SPI.transfer(addr_byte_2); - Prog_SPI.transfer(addr_byte_3); - Prog_SPI.transfer(addr_byte_0 ^ addr_byte_1 ^ addr_byte_2 ^ addr_byte_3); - ExitOnFail(spi_ack_frame()); - - uint8_t chk_sum = (len - 1) & 0xFF; - Prog_SPI.transfer((len - 1) & 0xFF); - for (size_t i = 0; i < len; i++) { - chk_sum ^= bytes[i]; - Prog_SPI.transfer(bytes[i]); - } - Prog_SPI.transfer(chk_sum); - ExitOnFail(spi_ack_frame()); - - return true; + Serial.println("init"); // had to add this; for some reason without it we don't pull in Print.cpp which causes a + // linker error because _write is not defined } -void loop() { - // TODO - prog state machine - // Wait for CAN cmds, control reset and boot - // Load flash files and flash over SPI - - // can.pump_events() or similar - // process commands - note that each command must be guaranteed to not halt - // TODO - figure out how to parse the incoming cmd and payload - uint8_t *raw_bytes; - size_t raw_msg_len; - - CAN_Msg_Decoder raw_msg(raw_bytes, raw_msg_len, prog_state); - - if (const auto msg = raw_msg.decode()) { - // TODO - this - - } else if (const auto msg = raw_msg.decode()) { - // TODO - enforce state - reset_h7(); - - } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_IDLE)) { - if (enter_bootloader()) { - prog_state = STATE_PRE_ERASE; - } else { - // TODO - handle error - } - - } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PRE_ERASE)) { - if (erase_memory()) { - prog_state = STATE_READY; - } else { - // TODO - handle error - } - - } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_READY)) { - prog_state = STATE_PAGE_SELECTED; - active_pg_addr = msg->page_addr; - for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { - chunk_rcv[i] = false; - } - - } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { - if (msg->page_addr == active_pg_addr) { - static_assert(sizeof(msg->flash_bytes) == CAN_CHUNK_SIZE); - memcpy(&page_cache[msg->chunk_addr * CAN_CHUNK_SIZE], msg->flash_bytes, CAN_CHUNK_SIZE); - chunk_rcv[msg->chunk_addr] = true; - } else { - // TODO - handle wrong page addr - } - - } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { - bool all_chunk_rcv = true; - for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { - if (!chunk_rcv) { - can_msg_request_mem_packet_t resp; - resp.chunk_addr = i; - resp.page_addr = active_pg_addr; - // TODO - send request message - all_chunk_rcv = false; - } - } - - if (all_chunk_rcv) { - for (size_t i = 0; i < NUM_WRITE_CHUNKS_PER_PAGE; i++) { - static_assert(WRITE_CHUNK_SIZE <= 256); - write_memory(active_pg_addr * PAGE_CACHE_SIZE + WRITE_CHUNK_SIZE * i, &page_cache[WRITE_CHUNK_SIZE * i], - WRITE_CHUNK_SIZE); - // TODO - send ok - } - prog_state = STATE_READY; - } - } - - raw_msg.send_error_if_not_decoded(); -} \ No newline at end of file +void loop() {} \ No newline at end of file From 37ff460c4bc11bcb6644986968153fa41762a4f8 Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Fri, 30 Oct 2026 12:05:26 -0400 Subject: [PATCH 2/9] started work on implementing CAN for G4 and moving platform-dependent non-HAL things out of the fdcan_toad --- firmware/boards/TOAD_G4/variant_TOAD_G4.h | 4 +++ firmware/lib/fdcan/fdcan_toad.cpp | 36 +++++++++++++++++----- firmware/lib/fdcan/fdcan_toad.h | 36 ++++------------------ firmware/lib/hardware_mapping/ec_pins.h | 5 +++ firmware/lib/hardware_mapping/prog_pins.h | 5 +++ firmware/lib/throttle_valves/MksServo57D.h | 1 + firmware/src/ec_main.cpp | 17 +++++++++- firmware/src/prog_main.cpp | 13 ++++++++ 8 files changed, 78 insertions(+), 39 deletions(-) diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.h b/firmware/boards/TOAD_G4/variant_TOAD_G4.h index 0d8808b..adf54ff 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.h +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.h @@ -203,6 +203,10 @@ #define HAL_QSPI_MODULE_ENABLED #endif +#if !defined(HAL_FDCAN_MODULE_DISABLED) // enable FDCAN HAL definitions +#define HAL_FDCAN_MODULE_ENABLED +#endif + /*---------------------------------------------------------------------------- * Arduino objects - C++ only *----------------------------------------------------------------------------*/ diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index 18eed69..b7008fa 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -2,18 +2,21 @@ #include "Arduino.h" +#if defined(TOAD_ENGINE_CONTROLLER_ONLY) #include "stm32h7xx_hal.h" #include "variant_TOAD_H7.h" +#include "ec_pins.h" +#elif defined(TOAD_PROGRAMMER_ONLY) +#include "stm32g474xx.h" +#include "variant_TOAD_G4.h" +#include "prog_pins.h" +#endif + #include "ec_pins.h" #include "CommsSerial.h" using namespace arduino; -CAN can_tvc(PIN_CAN_TVC_TX, PIN_CAN_TVC_RX); -CAN can_fc(PIN_CAN_FC_TX, PIN_CAN_FC_RX); - -std::array can_list = {&can_tvc, &can_fc}; - inline CAN* resolve_can(FDCAN_GlobalTypeDef* inst) { for (auto c : can_list) @@ -176,9 +179,16 @@ bool CAN::begin(CanBitRate _bitrate) this->hfdcan.Init.NominalSyncJumpWidth = 8U < seg_2_tq ? 8U : seg_2_tq; this->hfdcan.Init.NominalTimeSeg1 = seg_1_tq; this->hfdcan.Init.NominalTimeSeg2 = seg_2_tq; - this->hfdcan.Init.MessageRAMOffset = (this->hfdcan.Instance == FDCAN1 ? 0 : 1280); // FDCAN1 gets 0-1279, FDCAN2 gets 1280-2559 this->hfdcan.Init.StdFiltersNbr = 0; // not using any filters on this bus - that way CPU receives all messages (since we are just talking to actuators anyway, this is fine) this->hfdcan.Init.ExtFiltersNbr = 0; + this->hfdcan.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; // other option is queue operation; with fifo messages are sent in the order they are placed in the fifo, with queue they are sent in order of priority. i think we want fifo + + + // platform-dependent options: + // H7 forces the user to determine these values; on the G4 the HAL automatically decides RAM start address and buffer/fifo sizes + // see FDCAN_CalcultateRamBlockAddresses() of stm32g4xx_hal_fdcan.c + #if defined(TOAD_ENGINE_CONTROLLER_ONLY) + this->hfdcan.Init.MessageRAMOffset = (this->hfdcan.Instance == FDCAN1 ? 0 : 1280); // FDCAN1 gets 0-1279, FDCAN2 gets 1280-2559 this->hfdcan.Init.RxFifo0ElmtsNbr = 8; this->hfdcan.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8; this->hfdcan.Init.RxFifo1ElmtsNbr = 0; @@ -186,10 +196,10 @@ bool CAN::begin(CanBitRate _bitrate) this->hfdcan.Init.TxEventsNbr = 0; this->hfdcan.Init.TxBuffersNbr = 1; this->hfdcan.Init.TxFifoQueueElmtsNbr = 0; - this->hfdcan.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; // other option is queue operation; with fifo messages are sent in the order they are placed in the fifo, with queue they are sent in order of priority. i think we want fifo this->hfdcan.Init.TxElmtSize = FDCAN_DATA_BYTES_8; this->hfdcan.ErrorCallback = fdcan_error_cbk; this->hfdcan.ErrorStatusCallback = fdcan_error_status_cbk; + #endif // step (1) refers to steps as they are defined by the HAL reference @@ -355,16 +365,26 @@ int CAN::write(CanMsg const & msg) tx_header.TxEventFifoControl = FDCAN_NO_TX_EVENTS; // do not store transmission events in the TX event FIFO tx_header.MessageMarker = 0; + HAL_StatusTypeDef status; + + #if defined(TOAD_ENGINE_CONTROLLER_ONLY) + // NOTE: on the H7 this is done in two steps: (1) place the message in the buffer, (2) activate the transmission request // place the message in the first buffer - HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxBuffer(&(this->hfdcan), &tx_header, msg.data, FDCAN_TX_BUFFER0); + status = HAL_FDCAN_AddMessageToTxBuffer(&(this->hfdcan), &tx_header, msg.data, FDCAN_TX_BUFFER0); if (status != HAL_OK) { return 0; } + // enable transmission request status = HAL_FDCAN_EnableTxBufferRequest(&(this->hfdcan), FDCAN_TX_BUFFER0); + #elif defined(TOAD_PROGRAMMER_ONLY) + // On the G4 the HAL function HAL_FDCAN_AddMessageToTxFifoQ() both adds the message to the buffer or queue and activates the transmission request + status = HAL_FDCAN_AddMessageToTxFifoQ(&(this->hfdcan), &tx_header, msg.data); + #endif + return (status == HAL_OK) ? 1 : 0; } diff --git a/firmware/lib/fdcan/fdcan_toad.h b/firmware/lib/fdcan/fdcan_toad.h index 5401971..dedbff9 100644 --- a/firmware/lib/fdcan/fdcan_toad.h +++ b/firmware/lib/fdcan/fdcan_toad.h @@ -2,14 +2,15 @@ #include #include "api/HardwareCAN.h" #include "Arduino.h" -#include "stm32h7xx_hal.h" #include "CommandRouter.h" -class CAN; +#if defined(TOAD_ENGINE_CONTROLLER_ONLY) +#include "stm32h7xx_hal.h" +#elif defined(TOAD_PROGRAMMER_ONLY) +#include "stm32g474xx.h" +#endif -// instantiated in fdcan_toad.cpp -extern CAN can_tvc; -extern CAN can_fc; +class CAN; class CAN : public arduino::HardwareCAN { @@ -72,31 +73,6 @@ class CAN : public arduino::HardwareCAN { return &hfdcan; } - - static bool init(void) - { - bool success = true; - - // TODO - print error for each CAN bus that fails - success = success && can_tvc.begin(CanBitRate::BR_500k); - success = success && can_fc.begin(CanBitRate::BR_500k); - - if (success) - { - CommandRouter::add([]() - { - CAN& test_can = can_tvc; - - const uint8_t str[] = "hello123"; - arduino::CanMsg msg(0x55, 8, str); - - test_can.write(msg); - }, "can_loopback_test"); - }; - - return success; - } - private: FDCAN_Handle_Wrapper hfdcan = {0}; diff --git a/firmware/lib/hardware_mapping/ec_pins.h b/firmware/lib/hardware_mapping/ec_pins.h index 22f806e..cbe8333 100644 --- a/firmware/lib/hardware_mapping/ec_pins.h +++ b/firmware/lib/hardware_mapping/ec_pins.h @@ -6,6 +6,7 @@ #include "SPI.h" #include "ec_sensors.h" #include "ec_valves.h" +#include "fdcan_toad.h" // UARTS // Primary serial output (UART5) @@ -62,6 +63,10 @@ #define PIN_CAN_FC_RX PH14 #define PIN_CAN_FC_TX PH13 +extern CAN can_tvc; +extern CAN can_fc; +inline constexpr std::array can_list = {&can_tvc, &can_fc}; + // PWM (spark) #define PIN_SPARK_PWM PC6 #define PIN_SPARK_TRIG PC13 diff --git a/firmware/lib/hardware_mapping/prog_pins.h b/firmware/lib/hardware_mapping/prog_pins.h index 61a32c8..a3f7d7a 100644 --- a/firmware/lib/hardware_mapping/prog_pins.h +++ b/firmware/lib/hardware_mapping/prog_pins.h @@ -4,6 +4,8 @@ #include "SPI.h" +#include "fdcan_toad.h" + // TODO - audit these once EC design is finalized // UARTS @@ -25,6 +27,9 @@ #define PIN_CAN_GSE_RX PB12 #define PIN_CAN_GSE_TX PB13 +extern CAN can_gse; +inline constexpr std::array can_list = {&can_gse}; + // ID Pin #define PIN_PROG_ID PB1 #define PROG_ID_FLIGHT_CONTROLLER HIGH diff --git a/firmware/lib/throttle_valves/MksServo57D.h b/firmware/lib/throttle_valves/MksServo57D.h index a6f03d0..3ae8d24 100644 --- a/firmware/lib/throttle_valves/MksServo57D.h +++ b/firmware/lib/throttle_valves/MksServo57D.h @@ -3,6 +3,7 @@ #include "CommsSerial.h" #include "ErrorCounters.h" #include "fdcan_toad.h" +#include "ec_pins.h" // for can_tvc #include #include diff --git a/firmware/src/ec_main.cpp b/firmware/src/ec_main.cpp index 5011fc4..9870738 100644 --- a/firmware/src/ec_main.cpp +++ b/firmware/src/ec_main.cpp @@ -21,6 +21,9 @@ CommsSerial_t HW_FallbackSerial(PIN_HW_FALLBACK_SERIAL_RX, PIN_HW_FALLBACK SPIClass PT_TC_SPI_1(PIN_PT_TC_SPI_1_MOSI, PIN_PT_TC_SPI_1_MISO, PIN_PT_TC_SPI_1_SCK); SPIClass PT_TC_SPI_3(PIN_PT_TC_SPI_3_MOSI, PIN_PT_TC_SPI_3_MISO, PIN_PT_TC_SPI_3_SCK); +CAN can_tvc(PIN_CAN_TVC_TX, PIN_CAN_TVC_RX); +CAN can_fc(PIN_CAN_FC_TX, PIN_CAN_FC_RX); + bool kill_flag; bool arm_flag; void flight_loop(); @@ -46,7 +49,7 @@ void setup() { bool all_modules_ok = true; - all_modules_ok &= CAN::init(); + all_modules_ok &= PressureSensors::begin(); all_modules_ok &= TemperatureSensors::begin(); all_modules_ok &= RS485s::begin(); @@ -54,6 +57,8 @@ void setup() { all_modules_ok &= SolenoidValves::begin(); all_modules_ok &= TVC_Actuators::begin(); all_modules_ok &= ValveController::begin(); + all_modules_ok &= can_tvc.begin(CanBitRate::BR_500k); + all_modules_ok &= can_fc.begin(CanBitRate::BR_500k); if (!all_modules_ok) { while (true) { @@ -66,6 +71,16 @@ void setup() { CommandRouter::add(flight_loop, "start_flight_loop"); CommandRouter::add_flag(&kill_flag, "k", "terminate the flight loop early"); CommandRouter::add_flag(&arm_flag, "arm", "start following a trajectory"); + + CommandRouter::add([]() + { + CAN& test_can = can_tvc; + + const uint8_t str[] = "hello123"; + arduino::CanMsg msg(0x55, 8, str); + + test_can.write(msg); + }, "can_loopback_test"); } void loop() { diff --git a/firmware/src/prog_main.cpp b/firmware/src/prog_main.cpp index 02103d2..694b642 100644 --- a/firmware/src/prog_main.cpp +++ b/firmware/src/prog_main.cpp @@ -240,10 +240,23 @@ // #include "CommsSerial.h" #include +#include "prog_pins.h" + +CAN can_gse(PIN_CAN_GSE_TX, PIN_CAN_GSE_RX); void setup() { Serial.begin(9600); + if (!can_gse.begin(CanBitRate::BR_500k)) + { + for (;;) + { + Serial.println("CAN failed to begin()"); + + delay(1000); + } + } + Serial.println("init"); // had to add this; for some reason without it we don't pull in Print.cpp which causes a // linker error because _write is not defined } From 38e2bfbde19421349dd829be26a1f0065cc7de2f Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Fri, 30 Oct 2026 23:38:56 -0400 Subject: [PATCH 3/9] select FDCAN clock source and enable it's APB clock, CAN.begin() returns successfully now --- firmware/boards/TOAD_G4/variant_TOAD_G4.cpp | 12 ++++++++ firmware/boards/TOAD_G4/variant_TOAD_G4.h | 2 ++ firmware/lib/fdcan/fdcan_toad.cpp | 1 - firmware/lib/serial_comms/CommsSerial.h | 2 +- firmware/src/prog_main.cpp | 33 ++++++++++++++++++--- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp index 5b0efea..c4432a0 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp @@ -170,4 +170,16 @@ WEAK void SystemClock_Config(void) { /* Peripheral interrupt init */ HAL_NVIC_SetPriority(USB_LP_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USB_LP_IRQn); + + /* Select FDCAN Clock Source */ + PeriphClkInit = {0}; + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN; + PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL; + + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { + Error_Handler(); + } + + /* Enable FDCAN Peripheral Clock */ + __HAL_RCC_FDCAN_CLK_ENABLE(); } diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.h b/firmware/boards/TOAD_G4/variant_TOAD_G4.h index adf54ff..24e8469 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.h +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.h @@ -207,6 +207,8 @@ #define HAL_FDCAN_MODULE_ENABLED #endif +#define HSE_VALUE (8000000UL) /*!< Value of the External oscillator in Hz */ + /*---------------------------------------------------------------------------- * Arduino objects - C++ only *----------------------------------------------------------------------------*/ diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index b7008fa..b523fca 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -12,7 +12,6 @@ #include "prog_pins.h" #endif -#include "ec_pins.h" #include "CommsSerial.h" using namespace arduino; diff --git a/firmware/lib/serial_comms/CommsSerial.h b/firmware/lib/serial_comms/CommsSerial.h index 1800392..3ebffc5 100644 --- a/firmware/lib/serial_comms/CommsSerial.h +++ b/firmware/lib/serial_comms/CommsSerial.h @@ -72,4 +72,4 @@ extern CommsSerial_t USB_CommsSerial; extern CommsSerial_t HW_CommsSerial; extern CommsSerial_t HW_FallbackSerial; -#define CommsSerial HW_CommsSerial \ No newline at end of file +#define CommsSerial USB_CommsSerial \ No newline at end of file diff --git a/firmware/src/prog_main.cpp b/firmware/src/prog_main.cpp index 694b642..39fa256 100644 --- a/firmware/src/prog_main.cpp +++ b/firmware/src/prog_main.cpp @@ -241,24 +241,49 @@ // #include "CommsSerial.h" #include #include "prog_pins.h" +#include "CommsSerial.h" + +#define RADIO_BAUD 57600 + CAN can_gse(PIN_CAN_GSE_TX, PIN_CAN_GSE_RX); +CommsSerial_t USB_CommsSerial; +CommsSerial_t HW_CommsSerial(PIN_HW_COMM_SERIAL_RX, PIN_HW_COMM_SERIAL_TX); +CommsSerial_t HW_FallbackSerial(PIN_HW_FALLBACK_SERIAL_RX, PIN_HW_FALLBACK_SERIAL_TX); + void setup() { - Serial.begin(9600); + + USB_CommsSerial.begin(RADIO_BAUD); + HW_CommsSerial.begin(RADIO_BAUD); + HW_FallbackSerial.begin(RADIO_BAUD); + + // delay(5000); + + // while (!CommsSerial.available()) + // { + // CommsSerial.println("Press enter to .begin() CAN"); + // delay(1000); + // } if (!can_gse.begin(CanBitRate::BR_500k)) { for (;;) { - Serial.println("CAN failed to begin()"); + CommsSerial.println("CAN failed to begin()"); delay(1000); } } - Serial.println("init"); // had to add this; for some reason without it we don't pull in Print.cpp which causes a + CommsSerial.println("init"); // had to add this; for some reason without it we don't pull in Print.cpp which causes a // linker error because _write is not defined } -void loop() {} \ No newline at end of file +void loop() +{ + + CommsSerial.println("Hello, world!"); + + delay(1000); +} \ No newline at end of file From 12c07067f8af18a25a7e2137554648ced55fcfe8 Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Sat, 31 Oct 2026 01:02:26 -0400 Subject: [PATCH 4/9] testing stuffs; we can throw away this commit --- firmware/src/ec_main.cpp | 52 ++++++++++++++++++++++++++++++++------ firmware/src/prog_main.cpp | 25 ++++++++++-------- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/firmware/src/ec_main.cpp b/firmware/src/ec_main.cpp index 9870738..1e72045 100644 --- a/firmware/src/ec_main.cpp +++ b/firmware/src/ec_main.cpp @@ -50,14 +50,14 @@ void setup() { bool all_modules_ok = true; - all_modules_ok &= PressureSensors::begin(); - all_modules_ok &= TemperatureSensors::begin(); - all_modules_ok &= RS485s::begin(); - all_modules_ok &= ThrottleValves::begin(); - all_modules_ok &= SolenoidValves::begin(); - all_modules_ok &= TVC_Actuators::begin(); - all_modules_ok &= ValveController::begin(); - all_modules_ok &= can_tvc.begin(CanBitRate::BR_500k); + // all_modules_ok &= PressureSensors::begin(); + // all_modules_ok &= TemperatureSensors::begin(); + // all_modules_ok &= RS485s::begin(); + // all_modules_ok &= ThrottleValves::begin(); + // all_modules_ok &= SolenoidValves::begin(); + // all_modules_ok &= TVC_Actuators::begin(); + // all_modules_ok &= ValveController::begin(); + // all_modules_ok &= can_tvc.begin(CanBitRate::BR_500k); all_modules_ok &= can_fc.begin(CanBitRate::BR_500k); if (!all_modules_ok) { @@ -81,6 +81,42 @@ void setup() { test_can.write(msg); }, "can_loopback_test"); + + + CommandRouter::add([]() + { + static int count = 0; + while (!CommsSerial.available()) + { + CanMsg to_send(0x00, 8, (const uint8_t*)"EC->PROG"); + + if (can_fc.write(to_send) != 1) + { + CommsSerial.println("can_fc.write() failed"); + return; + } + + uint32_t start_ms = millis(); + + while (!can_fc.available()) + { + if (millis() - start_ms > 500) + { + CommsSerial.println("Error: reply timed out"); + return; + } + } + + while (can_fc.available()) + { + CanMsg msg = can_fc.read(); + + CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); + } + + delay(1000); + } + }, "gse_can_test"); } void loop() { diff --git a/firmware/src/prog_main.cpp b/firmware/src/prog_main.cpp index 39fa256..c831901 100644 --- a/firmware/src/prog_main.cpp +++ b/firmware/src/prog_main.cpp @@ -258,14 +258,6 @@ void setup() { HW_CommsSerial.begin(RADIO_BAUD); HW_FallbackSerial.begin(RADIO_BAUD); - // delay(5000); - - // while (!CommsSerial.available()) - // { - // CommsSerial.println("Press enter to .begin() CAN"); - // delay(1000); - // } - if (!can_gse.begin(CanBitRate::BR_500k)) { for (;;) @@ -282,8 +274,19 @@ void setup() { void loop() { - - CommsSerial.println("Hello, world!"); + static int count = 0; + if (can_gse.available()) + { + // receive and print out message + CanMsg msg = can_gse.read(); + + CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); + + // send a reply + CanMsg reply(0x01, 8, (const uint8_t*)"PROG->EC"); + + can_gse.write(reply); + } - delay(1000); + delay(50); } \ No newline at end of file From c5f11f1d532c0021c0e7518d0120a55cf0bdb186 Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Sat, 31 Oct 2026 17:55:32 -0400 Subject: [PATCH 5/9] consolidate tests to fdcan_toad files and remove interrupt handlers because we don't need them/they complicate the codebase --- firmware/boards/TOAD_H7/variant_TOAD_H7.h | 3 - firmware/lib/fdcan/fdcan_toad.cpp | 177 +++++++++------------- firmware/lib/fdcan/fdcan_toad.h | 55 +++++-- firmware/src/ec_main.cpp | 51 +------ 4 files changed, 116 insertions(+), 170 deletions(-) diff --git a/firmware/boards/TOAD_H7/variant_TOAD_H7.h b/firmware/boards/TOAD_H7/variant_TOAD_H7.h index ffe9e07..67b1e86 100644 --- a/firmware/boards/TOAD_H7/variant_TOAD_H7.h +++ b/firmware/boards/TOAD_H7/variant_TOAD_H7.h @@ -299,9 +299,6 @@ #if !defined (HAL_FDCAN_MODULE_ENABLED) #define HAL_FDCAN_MODULE_ENABLED #endif -#if !defined (USE_HAL_FDCAN_REGISTER_CALLBACKS) -#define USE_HAL_FDCAN_REGISTER_CALLBACKS 1 -#endif #if !defined(HSE_VALUE) diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index b523fca..ad431b2 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -2,13 +2,17 @@ #include "Arduino.h" -#if defined(TOAD_ENGINE_CONTROLLER_ONLY) +#if defined(STM32H7) #include "stm32h7xx_hal.h" #include "variant_TOAD_H7.h" -#include "ec_pins.h" -#elif defined(TOAD_PROGRAMMER_ONLY) +#elif defined(STM32G4) #include "stm32g474xx.h" #include "variant_TOAD_G4.h" +#endif + +#if defined(TOAD_ENGINE_CONTROLLER_ONLY) +#include "ec_pins.h" +#elif defined(TOAD_PROGRAMMER_ONLY) #include "prog_pins.h" #endif @@ -39,49 +43,6 @@ static void CAN_init_gpio_dynamic(uint32_t pin, const PinMap pin_map[]) pin_function(pin_name, pinmap_function(pin_name, pin_map)); } -extern "C" -{ - -void fdcan_error_cbk(FDCAN_HandleTypeDef* hfdcan) -{ - // FDCAN_Handle_Wrapper inherits FDCAN_HandleTypeDef and all our hfdcans are defined as FDCAN_Handle_Wrapper - // therefore this cast is safe - auto target = static_cast(hfdcan); - - if (target == nullptr) - { - return; - } - - target->obj->error_update_from_isr(); - return; -} - -void fdcan_error_status_cbk(FDCAN_HandleTypeDef* hfdcan, uint32_t ErrorStatusITs) -{ - fdcan_error_cbk(hfdcan); -} - - -void FDCAN1_IT0_IRQHandler(void) -{ - if (auto can_ptr = resolve_can(FDCAN1); can_ptr != nullptr) - { - HAL_FDCAN_IRQHandler(can_ptr->handle()); - } -} - - -void FDCAN2_IT0_IRQHandler(void) -{ - if (auto can_ptr = resolve_can(FDCAN2); can_ptr != nullptr) - { - HAL_FDCAN_IRQHandler(can_ptr->handle()); - } -} - -} // extern "C" - CAN::CAN(uint32_t _tx_pin, uint32_t _rx_pin) { @@ -186,7 +147,7 @@ bool CAN::begin(CanBitRate _bitrate) // platform-dependent options: // H7 forces the user to determine these values; on the G4 the HAL automatically decides RAM start address and buffer/fifo sizes // see FDCAN_CalcultateRamBlockAddresses() of stm32g4xx_hal_fdcan.c - #if defined(TOAD_ENGINE_CONTROLLER_ONLY) + #if defined(STM32H7) this->hfdcan.Init.MessageRAMOffset = (this->hfdcan.Instance == FDCAN1 ? 0 : 1280); // FDCAN1 gets 0-1279, FDCAN2 gets 1280-2559 this->hfdcan.Init.RxFifo0ElmtsNbr = 8; this->hfdcan.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8; @@ -196,8 +157,6 @@ bool CAN::begin(CanBitRate _bitrate) this->hfdcan.Init.TxBuffersNbr = 1; this->hfdcan.Init.TxFifoQueueElmtsNbr = 0; this->hfdcan.Init.TxElmtSize = FDCAN_DATA_BYTES_8; - this->hfdcan.ErrorCallback = fdcan_error_cbk; - this->hfdcan.ErrorStatusCallback = fdcan_error_status_cbk; #endif @@ -217,18 +176,6 @@ bool CAN::begin(CanBitRate _bitrate) CAN_init_gpio_dynamic(tx_pin, PinMap_CAN_TD); CAN_init_gpio_dynamic(rx_pin, PinMap_CAN_RD); - - IRQn_Type irqn = FDCAN1_IT0_IRQn; // each FDCAN has two interrupt lines but we can just use line 0 - - if (hfdcan.Instance == FDCAN2) - { - irqn = FDCAN2_IT0_IRQn; - } - - /* FDCAN interrupt Init */ - HAL_NVIC_SetPriority(irqn, 0, 0); - HAL_NVIC_EnableIRQ(irqn); - // step 1 end // step 2: configure the FDCAN peripheral @@ -240,22 +187,6 @@ bool CAN::begin(CanBitRate _bitrate) return false; } - constexpr uint32_t it_list = FDCAN_IT_ERROR_PASSIVE | FDCAN_IT_ERROR_WARNING; - - if (HAL_FDCAN_ConfigInterruptLines(&(this->hfdcan), it_list, FDCAN_INTERRUPT_LINE0) != HAL_OK) - { - return false; - } - - if (HAL_FDCAN_ActivateNotification( - &(this->hfdcan), - it_list, // enables error passive and error warning interrupt notifications - 0x00 - ) != HAL_OK) - { - return false; - } - // step 2 end // step 3: start the FDCAN @@ -366,7 +297,7 @@ int CAN::write(CanMsg const & msg) HAL_StatusTypeDef status; - #if defined(TOAD_ENGINE_CONTROLLER_ONLY) + #if defined(STM32H7) // NOTE: on the H7 this is done in two steps: (1) place the message in the buffer, (2) activate the transmission request // place the message in the first buffer status = HAL_FDCAN_AddMessageToTxBuffer(&(this->hfdcan), &tx_header, msg.data, FDCAN_TX_BUFFER0); @@ -379,7 +310,7 @@ int CAN::write(CanMsg const & msg) // enable transmission request status = HAL_FDCAN_EnableTxBufferRequest(&(this->hfdcan), FDCAN_TX_BUFFER0); - #elif defined(TOAD_PROGRAMMER_ONLY) + #elif defined(STM32G4) // On the G4 the HAL function HAL_FDCAN_AddMessageToTxFifoQ() both adds the message to the buffer or queue and activates the transmission request status = HAL_FDCAN_AddMessageToTxFifoQ(&(this->hfdcan), &tx_header, msg.data); #endif @@ -436,46 +367,74 @@ void CAN::end(void) return; } -void CAN::set_error_cbk(CAN_error_cbk_t error_cbk) -{ - // this should be an atomic operation, so there is no need to guard - this->error_cbk = error_cbk; - return; -} -// note: must only be called from an interrupt context, from a single isr -void CAN::error_update_from_isr(void) +void CAN::cmd_can_send_test(void) { - if (this->hfdcan.Instance == nullptr) + static int count = 0; + while (!CommsSerial.available()) { - return; - } + CanMsg to_send(0x00, 8, (const uint8_t*)"HELLO123"); - FDCAN_ErrorCountersTypeDef new_error_counts = {0}; + if (this->write(to_send) != 1) + { + CommsSerial.println("can.write() failed"); + return; + } - HAL_FDCAN_GetErrorCounters(&(this->hfdcan), &new_error_counts); + uint32_t start_ms = millis(); - if (this->error_cbk == nullptr) - { - memcpy(&(this->error_counts), &new_error_counts, sizeof(new_error_counts)); - return; - } + while (!this->available()) + { + if (millis() - start_ms > 500) + { + CommsSerial.println("Error: reply timed out"); + return; + } + } - if (new_error_counts.RxErrorCnt != this->error_counts.RxErrorCnt) - { - this->error_cbk(CAN::error_type_t::RX_ERROR, new_error_counts.RxErrorCnt); - } + while (this->available()) + { + CanMsg msg = this->read(); - if (new_error_counts.RxErrorPassive != this->error_counts.RxErrorPassive) - { - this->error_cbk(CAN::error_type_t::RX_ERROR_PASSIVE, new_error_counts.RxErrorPassive); + CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); + } + + delay(1000); } +} - if (new_error_counts.TxErrorCnt != this->error_counts.TxErrorCnt) +void CAN::cmd_can_receive_test(void) +{ + static int count = 0; + while (!CommsSerial.available()) { - this->error_cbk(CAN::error_type_t::TX_ERROR, new_error_counts.TxErrorCnt); - } + uint32_t start_ms = millis(); - memcpy(&(this->error_counts), &new_error_counts, sizeof(new_error_counts)); - return; + while (!this->available()) + { + if (millis() - start_ms > 5000) + { + CommsSerial.println("Error: receive timed out"); + return; + } + } + + while (this->available()) + { + CanMsg msg = this->read(); + + CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); + } + + + CanMsg to_send(0x00, 8, (const uint8_t*)"REPLY123"); + + if (this->write(to_send) != 1) + { + CommsSerial.println("can.write() failed"); + return; + } + + delay(100); + } } \ No newline at end of file diff --git a/firmware/lib/fdcan/fdcan_toad.h b/firmware/lib/fdcan/fdcan_toad.h index dedbff9..1d23d42 100644 --- a/firmware/lib/fdcan/fdcan_toad.h +++ b/firmware/lib/fdcan/fdcan_toad.h @@ -4,9 +4,9 @@ #include "Arduino.h" #include "CommandRouter.h" -#if defined(TOAD_ENGINE_CONTROLLER_ONLY) +#if defined(STM32H7) #include "stm32h7xx_hal.h" -#elif defined(TOAD_PROGRAMMER_ONLY) +#elif defined(STM32G4) #include "stm32g474xx.h" #endif @@ -24,8 +24,6 @@ class CAN : public arduino::HardwareCAN RX_ERROR_PASSIVE }; - typedef void (*CAN_error_cbk_t)(error_type_t error_type, uint32_t new_count); - // Initialize using standard Arduino CanBitRate enum. // Conforms to arduino::HardwareCAN interface. // Returns true on success, false on failure. @@ -52,17 +50,16 @@ class CAN : public arduino::HardwareCAN // Conforms to arduino::HardwareCAN interface. void end(void) override; - // Register a callback for CAN bus error updates. - void set_error_cbk(CAN_error_cbk_t error_cbk); - - // Internal handler called from interrupt service routine. - // Note: Must only be called from an interrupt context, without nested interrupts. - void error_update_from_isr(void); - // Wait for ongoing transmission in TX buffer 0 to complete. // Returns true if buffer is free, false if timed out. bool flush(uint32_t timeout_ms = UINT32_MAX); + /* Test sending data on one CAN, and expect to receive data after every message */ + void cmd_can_send_test(void); + + /* Test receiving data on one CAN, and send a frame after every received message */ + void cmd_can_receive_test(void); + struct FDCAN_Handle_Wrapper : public FDCAN_HandleTypeDef { CAN* obj; // pointer back to the CAN object that owns this peripheral @@ -81,5 +78,39 @@ class CAN : public arduino::HardwareCAN uint32_t tx_pin = 0; uint32_t rx_pin = 0; uint8_t rx_data[64] = {0}; - CAN_error_cbk_t error_cbk = nullptr; }; + + +template +void cmd_can_loopback(void) +{ + // repeat until the user presses a button + while (!CommsSerial.available()) + { + unsigned long count = 0; + CommsSerial.printf("Test %u:\n", count); + CanMsg test_msg(0x123, 8, (const uint8_t*)"HELLO123"); + + sender.write(test_msg); + + uint32_t start_ms = millis(); + while (!receiver.available()) + { + if (millis() - start_ms > 500) + { + CommsSerial.println("Receive timed out"); + return; + } + } + + while (receiver.available()) + { + CanMsg received = receiver.read(); + + CommsSerial.printf("Received message: ID: 0x%04X\tDLC: %2d\tData: %.*s\n", received.id, received.data_length, received.data_length, (const char*)received.data); + } + + delay(500); + ++count; + } +} \ No newline at end of file diff --git a/firmware/src/ec_main.cpp b/firmware/src/ec_main.cpp index 1e72045..6db794a 100644 --- a/firmware/src/ec_main.cpp +++ b/firmware/src/ec_main.cpp @@ -57,7 +57,7 @@ void setup() { // all_modules_ok &= SolenoidValves::begin(); // all_modules_ok &= TVC_Actuators::begin(); // all_modules_ok &= ValveController::begin(); - // all_modules_ok &= can_tvc.begin(CanBitRate::BR_500k); + all_modules_ok &= can_tvc.begin(CanBitRate::BR_500k); all_modules_ok &= can_fc.begin(CanBitRate::BR_500k); if (!all_modules_ok) { @@ -72,51 +72,10 @@ void setup() { CommandRouter::add_flag(&kill_flag, "k", "terminate the flight loop early"); CommandRouter::add_flag(&arm_flag, "arm", "start following a trajectory"); - CommandRouter::add([]() - { - CAN& test_can = can_tvc; - - const uint8_t str[] = "hello123"; - arduino::CanMsg msg(0x55, 8, str); - - test_can.write(msg); - }, "can_loopback_test"); - - - CommandRouter::add([]() - { - static int count = 0; - while (!CommsSerial.available()) - { - CanMsg to_send(0x00, 8, (const uint8_t*)"EC->PROG"); - - if (can_fc.write(to_send) != 1) - { - CommsSerial.println("can_fc.write() failed"); - return; - } - - uint32_t start_ms = millis(); - - while (!can_fc.available()) - { - if (millis() - start_ms > 500) - { - CommsSerial.println("Error: reply timed out"); - return; - } - } - - while (can_fc.available()) - { - CanMsg msg = can_fc.read(); - - CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); - } - - delay(1000); - } - }, "gse_can_test"); + CommandRouter::add(cmd_can_loopback, "tvc_to_fc_test"); + CommandRouter::add(cmd_can_loopback, "fc_to_tvc_test"); + CommandRouter::add([](){can_fc.cmd_can_send_test();}, "can_send_test"); + CommandRouter::add([](){can_tvc.cmd_can_receive_test();}, "can_receive_test"); } void loop() { From 672e419b675d2fca86267fb3efdd7e8c3f009cb5 Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Sat, 31 Oct 2026 18:29:55 -0400 Subject: [PATCH 6/9] working toward min-diff --- firmware/boards/TOAD_G4/variant_TOAD_G4.cpp | 2 +- firmware/lib/fdcan/fdcan_toad.cpp | 19 ---------- firmware/lib/hardware_mapping/ec_pins.h | 1 - firmware/lib/hardware_mapping/prog_pins.h | 1 - firmware/src/ec_main.cpp | 42 ++++++++++++++------- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp index c4432a0..bcaa166 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp @@ -103,7 +103,7 @@ const uint32_t analogInputPin[] = { * @param None * @retval None */ -WEAK void SystemClock_Config(void) { +void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_CRSInitTypeDef pInit = {0}; diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index ad431b2..ca02e42 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -10,29 +10,10 @@ #include "variant_TOAD_G4.h" #endif -#if defined(TOAD_ENGINE_CONTROLLER_ONLY) -#include "ec_pins.h" -#elif defined(TOAD_PROGRAMMER_ONLY) -#include "prog_pins.h" -#endif - #include "CommsSerial.h" using namespace arduino; -inline CAN* resolve_can(FDCAN_GlobalTypeDef* inst) -{ - for (auto c : can_list) - { - if (c->handle()->Instance == inst) - { - return c; - } - } - - return nullptr; -} - /* enable GPIO clock and configure pin (must pass a bit mask for which pin(s) to configure on the specific port) */ static void CAN_init_gpio_dynamic(uint32_t pin, const PinMap pin_map[]) { diff --git a/firmware/lib/hardware_mapping/ec_pins.h b/firmware/lib/hardware_mapping/ec_pins.h index cbe8333..0607edc 100644 --- a/firmware/lib/hardware_mapping/ec_pins.h +++ b/firmware/lib/hardware_mapping/ec_pins.h @@ -65,7 +65,6 @@ extern CAN can_tvc; extern CAN can_fc; -inline constexpr std::array can_list = {&can_tvc, &can_fc}; // PWM (spark) #define PIN_SPARK_PWM PC6 diff --git a/firmware/lib/hardware_mapping/prog_pins.h b/firmware/lib/hardware_mapping/prog_pins.h index a3f7d7a..f5d2e42 100644 --- a/firmware/lib/hardware_mapping/prog_pins.h +++ b/firmware/lib/hardware_mapping/prog_pins.h @@ -28,7 +28,6 @@ #define PIN_CAN_GSE_TX PB13 extern CAN can_gse; -inline constexpr std::array can_list = {&can_gse}; // ID Pin #define PIN_PROG_ID PB1 diff --git a/firmware/src/ec_main.cpp b/firmware/src/ec_main.cpp index 6db794a..752f2d8 100644 --- a/firmware/src/ec_main.cpp +++ b/firmware/src/ec_main.cpp @@ -27,6 +27,7 @@ CAN can_fc(PIN_CAN_FC_TX, PIN_CAN_FC_RX); bool kill_flag; bool arm_flag; void flight_loop(); +bool can_begin(void); void setup() { // All shared interfaces are begun here. @@ -50,15 +51,15 @@ void setup() { bool all_modules_ok = true; - // all_modules_ok &= PressureSensors::begin(); - // all_modules_ok &= TemperatureSensors::begin(); - // all_modules_ok &= RS485s::begin(); - // all_modules_ok &= ThrottleValves::begin(); - // all_modules_ok &= SolenoidValves::begin(); - // all_modules_ok &= TVC_Actuators::begin(); - // all_modules_ok &= ValveController::begin(); - all_modules_ok &= can_tvc.begin(CanBitRate::BR_500k); - all_modules_ok &= can_fc.begin(CanBitRate::BR_500k); + all_modules_ok &= can_begin(); + all_modules_ok &= RS485s::begin(); + all_modules_ok &= PressureSensors::begin(); + all_modules_ok &= TemperatureSensors::begin(); + all_modules_ok &= ThrottleValves::begin(); + all_modules_ok &= SolenoidValves::begin(); + all_modules_ok &= TVC_Actuators::begin(); + all_modules_ok &= ValveController::begin(); + if (!all_modules_ok) { while (true) { @@ -72,10 +73,6 @@ void setup() { CommandRouter::add_flag(&kill_flag, "k", "terminate the flight loop early"); CommandRouter::add_flag(&arm_flag, "arm", "start following a trajectory"); - CommandRouter::add(cmd_can_loopback, "tvc_to_fc_test"); - CommandRouter::add(cmd_can_loopback, "fc_to_tvc_test"); - CommandRouter::add([](){can_fc.cmd_can_send_test();}, "can_send_test"); - CommandRouter::add([](){can_tvc.cmd_can_receive_test();}, "can_receive_test"); } void loop() { @@ -130,3 +127,22 @@ void flight_loop() { ThrottleValves::stop(); RCS::close(); } + + +bool can_begin(void) +{ + bool is_ok = true; + + is_ok &= can_tvc.begin(CanBitRate::BR_500k); + is_ok &= can_fc.begin(CanBitRate::BR_500k); + + if (is_ok) + { + CommandRouter::add(cmd_can_loopback, "tvc_to_fc_test"); + CommandRouter::add(cmd_can_loopback, "fc_to_tvc_test"); + CommandRouter::add([](){can_fc.cmd_can_send_test();}, "can_send_test"); + CommandRouter::add([](){can_tvc.cmd_can_receive_test();}, "can_receive_test"); + } + + return is_ok; +} \ No newline at end of file From b9dc9d7d5ef121fa5aed197626833576679dba28 Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Sat, 31 Oct 2026 21:41:54 -0400 Subject: [PATCH 7/9] get rid of USB interrupt NVIC config in the G4 SystemClock_Config; run formatter on modified sources; make fdcan_toad give a compiler error when neither STM32H7 or STM32G4 are defined --- firmware/boards/TOAD_G4/variant_TOAD_G4.cpp | 13 +- firmware/boards/TOAD_H7/variant_TOAD_H7.cpp | 6 +- firmware/boards/TOAD_H7/variant_TOAD_H7.h | 3 +- firmware/lib/fdcan/fdcan_toad.cpp | 222 ++++----- firmware/lib/fdcan/fdcan_toad.h | 149 +++--- firmware/src/ec_main.cpp | 16 +- firmware/src/prog_main.cpp | 481 +++++++++----------- 7 files changed, 380 insertions(+), 510 deletions(-) diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp index bcaa166..fdffd50 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp @@ -104,9 +104,10 @@ const uint32_t analogInputPin[] = { * @retval None */ void SystemClock_Config(void) { - RCC_OscInitTypeDef RCC_OscInitStruct = {0}; - RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; - RCC_CRSInitTypeDef pInit = {0}; + RCC_OscInitTypeDef RCC_OscInitStruct = {}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {}; + RCC_CRSInitTypeDef pInit = {}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {}; /** Configure the main internal regulator output voltage */ @@ -145,8 +146,6 @@ void SystemClock_Config(void) { */ __HAL_RCC_CRS_CLK_ENABLE(); - RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; - /** Configures CRS */ pInit.Prescaler = RCC_CRS_SYNC_DIV1; @@ -167,10 +166,6 @@ void SystemClock_Config(void) { /* Peripheral clock enable */ __HAL_RCC_USB_CLK_ENABLE(); - /* Peripheral interrupt init */ - HAL_NVIC_SetPriority(USB_LP_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(USB_LP_IRQn); - /* Select FDCAN Clock Source */ PeriphClkInit = {0}; PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN; diff --git a/firmware/boards/TOAD_H7/variant_TOAD_H7.cpp b/firmware/boards/TOAD_H7/variant_TOAD_H7.cpp index 78fa31c..ce0abd3 100644 --- a/firmware/boards/TOAD_H7/variant_TOAD_H7.cpp +++ b/firmware/boards/TOAD_H7/variant_TOAD_H7.cpp @@ -193,8 +193,7 @@ const uint32_t analogInputPin[] = { 140 // A31, PC3_C }; -void SystemClock_Config(void) -{ +void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; @@ -231,7 +230,8 @@ void SystemClock_Config(void) /** Initializes the CPU, AHB and APB buses clocks */ - RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1; + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | + RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; diff --git a/firmware/boards/TOAD_H7/variant_TOAD_H7.h b/firmware/boards/TOAD_H7/variant_TOAD_H7.h index 67b1e86..95cf283 100644 --- a/firmware/boards/TOAD_H7/variant_TOAD_H7.h +++ b/firmware/boards/TOAD_H7/variant_TOAD_H7.h @@ -296,11 +296,10 @@ #endif // added by jhillman - do compile FDCAN HAL -#if !defined (HAL_FDCAN_MODULE_ENABLED) +#if !defined(HAL_FDCAN_MODULE_ENABLED) #define HAL_FDCAN_MODULE_ENABLED #endif - #if !defined(HSE_VALUE) // our HSE is 8MHz // #define HSE_VALUE (8000000U) diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index ca02e42..d2a1f2d 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -2,21 +2,12 @@ #include "Arduino.h" -#if defined(STM32H7) -#include "stm32h7xx_hal.h" -#include "variant_TOAD_H7.h" -#elif defined(STM32G4) -#include "stm32g474xx.h" -#include "variant_TOAD_G4.h" -#endif - #include "CommsSerial.h" using namespace arduino; /* enable GPIO clock and configure pin (must pass a bit mask for which pin(s) to configure on the specific port) */ -static void CAN_init_gpio_dynamic(uint32_t pin, const PinMap pin_map[]) -{ +static void CAN_init_gpio_dynamic(uint32_t pin, const PinMap pin_map[]) { PinName pin_name = digitalPinToPinName(pin); @@ -24,34 +15,31 @@ static void CAN_init_gpio_dynamic(uint32_t pin, const PinMap pin_map[]) pin_function(pin_name, pinmap_function(pin_name, pin_map)); } - -CAN::CAN(uint32_t _tx_pin, uint32_t _rx_pin) -{ +CAN::CAN(uint32_t _tx_pin, uint32_t _rx_pin) { this->tx_pin = _tx_pin; this->rx_pin = _rx_pin; this->hfdcan = {0}; this->hfdcan.obj = this; } - -// jhillman adapted from https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H743I-EVAL/Examples/FDCAN/FDCAN_Classic_Frame_Networking/Src/main.c -bool CAN::begin(CanBitRate _bitrate) -{ +// jhillman adapted from +// https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H743I-EVAL/Examples/FDCAN/FDCAN_Classic_Frame_Networking/Src/main.c +bool CAN::begin(CanBitRate _bitrate) { uint32_t bit_rate = (uint32_t)_bitrate; - FDCAN_GlobalTypeDef* inst_1 = static_cast(pinmap_find_peripheral(digitalPinToPinName(tx_pin), PinMap_CAN_TD)); - FDCAN_GlobalTypeDef* inst_2 = static_cast(pinmap_find_peripheral(digitalPinToPinName(rx_pin), PinMap_CAN_RD)); + FDCAN_GlobalTypeDef *inst_1 = + static_cast(pinmap_find_peripheral(digitalPinToPinName(tx_pin), PinMap_CAN_TD)); + FDCAN_GlobalTypeDef *inst_2 = + static_cast(pinmap_find_peripheral(digitalPinToPinName(rx_pin), PinMap_CAN_RD)); - if ((inst_1 != inst_2) || inst_1 == NP) - { + if ((inst_1 != inst_2) || inst_1 == NP) { CommsSerial.println("Error: CAN has conflicting pins"); return false; } this->hfdcan.Instance = inst_1; - if (bit_rate == 0U || bit_rate > 1'000'000U) - { + if (bit_rate == 0U || bit_rate > 1'000'000U) { // not allowed for normal CAN operation CommsSerial.println("Error: invalid CAN bit rate supplied"); return false; @@ -59,15 +47,14 @@ bool CAN::begin(CanBitRate _bitrate) const uint32_t can_ker_ck = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_FDCAN); - if (can_ker_ck != 120'000'000) - { + if (can_ker_ck != 120'000'000) { // something went wrong, I was expecting 120 MHz CommsSerial.println("Error: CAN kernel clock frequency is not 120MHz"); CommsSerial.printf("Actual value: %u\n", can_ker_ck); return false; } - const uint32_t prescaler = 3; // or dynamic + const uint32_t prescaler = 3; // or dynamic const uint32_t tq_freq = can_ker_ck / prescaler; // Check if rate divides evenly without any remainder @@ -89,22 +76,18 @@ bool CAN::begin(CanBitRate _bitrate) uint32_t seg_1_tq; uint32_t seg_2_tq; - if (tq_before_sample > 0) - { + if (tq_before_sample > 0) { /* NominalTimeSeg1 = Propagation_segment + Phase_segment_1 */ // sample point is after (1 tq + NominalTimeSeg1) so we have to subtract 1tq here seg_1_tq = tq_before_sample - 1; - } - else - { + } else { CommsSerial.println("Error: tq_before_sample is zero"); return false; } seg_2_tq = bit_time_tq - tq_before_sample; - if (seg_1_tq > 256 || seg_2_tq > 128 || seg_1_tq < 2 || seg_2_tq < 2) - { + if (seg_1_tq > 256 || seg_2_tq > 128 || seg_1_tq < 2 || seg_2_tq < 2) { CommsSerial.println("Error: seg_1 or seg_2 calculated values are invalid"); return false; } @@ -120,16 +103,20 @@ bool CAN::begin(CanBitRate _bitrate) this->hfdcan.Init.NominalSyncJumpWidth = 8U < seg_2_tq ? 8U : seg_2_tq; this->hfdcan.Init.NominalTimeSeg1 = seg_1_tq; this->hfdcan.Init.NominalTimeSeg2 = seg_2_tq; - this->hfdcan.Init.StdFiltersNbr = 0; // not using any filters on this bus - that way CPU receives all messages (since we are just talking to actuators anyway, this is fine) + this->hfdcan.Init.StdFiltersNbr = 0; // not using any filters on this bus - that way CPU receives all messages (since + // we are just talking to actuators anyway, this is fine) this->hfdcan.Init.ExtFiltersNbr = 0; - this->hfdcan.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; // other option is queue operation; with fifo messages are sent in the order they are placed in the fifo, with queue they are sent in order of priority. i think we want fifo - - - // platform-dependent options: - // H7 forces the user to determine these values; on the G4 the HAL automatically decides RAM start address and buffer/fifo sizes - // see FDCAN_CalcultateRamBlockAddresses() of stm32g4xx_hal_fdcan.c - #if defined(STM32H7) - this->hfdcan.Init.MessageRAMOffset = (this->hfdcan.Instance == FDCAN1 ? 0 : 1280); // FDCAN1 gets 0-1279, FDCAN2 gets 1280-2559 + this->hfdcan.Init.TxFifoQueueMode = + FDCAN_TX_FIFO_OPERATION; // other option is queue operation; with fifo messages are sent in the order they are + // placed in the fifo, with queue they are sent in order of priority. i think we want + // fifo + +// platform-dependent options: +// H7 forces the user to determine these values; on the G4 the HAL automatically decides RAM start address and +// buffer/fifo sizes see FDCAN_CalcultateRamBlockAddresses() of stm32g4xx_hal_fdcan.c +#if defined(STM32H7) + this->hfdcan.Init.MessageRAMOffset = + (this->hfdcan.Instance == FDCAN1 ? 0 : 1280); // FDCAN1 gets 0-1279, FDCAN2 gets 1280-2559 this->hfdcan.Init.RxFifo0ElmtsNbr = 8; this->hfdcan.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8; this->hfdcan.Init.RxFifo1ElmtsNbr = 0; @@ -138,8 +125,7 @@ bool CAN::begin(CanBitRate _bitrate) this->hfdcan.Init.TxBuffersNbr = 1; this->hfdcan.Init.TxFifoQueueElmtsNbr = 0; this->hfdcan.Init.TxElmtSize = FDCAN_DATA_BYTES_8; - #endif - +#endif // step (1) refers to steps as they are defined by the HAL reference @@ -147,8 +133,7 @@ bool CAN::begin(CanBitRate _bitrate) // pass along the which pins to configure to the MspInit(), which is called during HAL_FDCAN_Init - if (HAL_FDCAN_Init(&(this->hfdcan)) != HAL_OK) - { + if (HAL_FDCAN_Init(&(this->hfdcan)) != HAL_OK) { /* Initialization Error */ CommsSerial.println("Error: HAL_FDCAN_Init failed"); return false; @@ -162,9 +147,10 @@ bool CAN::begin(CanBitRate _bitrate) // step 2: configure the FDCAN peripheral // filter creation should go around here, but we are not using any (no filtering) - /* Configure global filter to accept all 11 bit ID frames (and reject remote frames); jhillman todo: confirm the hardware on the bus does not use remote frames */ - if (HAL_FDCAN_ConfigGlobalFilter(&(this->hfdcan), FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE) != HAL_OK) - { + /* Configure global filter to accept all 11 bit ID frames (and reject remote frames); jhillman todo: confirm the + * hardware on the bus does not use remote frames */ + if (HAL_FDCAN_ConfigGlobalFilter(&(this->hfdcan), FDCAN_ACCEPT_IN_RX_FIFO0, FDCAN_ACCEPT_IN_RX_FIFO0, + FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE) != HAL_OK) { return false; } @@ -172,8 +158,7 @@ bool CAN::begin(CanBitRate _bitrate) // step 3: start the FDCAN /* Start the FDCAN module */ - if (HAL_FDCAN_Start(&(this->hfdcan)) != HAL_OK) - { + if (HAL_FDCAN_Start(&(this->hfdcan)) != HAL_OK) { /* Start Error */ return false; } @@ -184,41 +169,31 @@ bool CAN::begin(CanBitRate _bitrate) } // get number of elements in the receive fifo -size_t CAN::available(void) -{ - if (this->hfdcan.Instance == nullptr) - { +size_t CAN::available(void) { + if (this->hfdcan.Instance == nullptr) { return 0; } return HAL_FDCAN_GetRxFifoFillLevel(&(this->hfdcan), FDCAN_RX_FIFO0); } - // return 1 if the buffer is empty, 0 otherwise -uint32_t CAN::tx_free_count(void) -{ - if (this->hfdcan.Instance == nullptr) - { +uint32_t CAN::tx_free_count(void) { + if (this->hfdcan.Instance == nullptr) { return 0; } return 1 - HAL_FDCAN_IsTxBufferMessagePending(&(this->hfdcan), FDCAN_TX_BUFFER0); } - -bool CAN::flush(uint32_t timeout_ms) -{ - if (this->hfdcan.Instance == nullptr) - { +bool CAN::flush(uint32_t timeout_ms) { + if (this->hfdcan.Instance == nullptr) { return false; } uint32_t start_time = millis(); bool success = false; - while (millis() - start_time < timeout_ms || timeout_ms == UINT32_MAX) - { - if (HAL_FDCAN_IsTxBufferMessagePending(&(this->hfdcan), FDCAN_TX_BUFFER0) != 1) - { + while (millis() - start_time < timeout_ms || timeout_ms == UINT32_MAX) { + if (HAL_FDCAN_IsTxBufferMessagePending(&(this->hfdcan), FDCAN_TX_BUFFER0) != 1) { success = true; break; } @@ -226,41 +201,33 @@ bool CAN::flush(uint32_t timeout_ms) return success; } -int CAN::write(CanMsg const & msg) -{ - if (this->hfdcan.Instance == nullptr) - { +int CAN::write(CanMsg const &msg) { + if (this->hfdcan.Instance == nullptr) { return 0; } FDCAN_TxHeaderTypeDef tx_header = {0}; /* 1. wait for ongoing transmission to complete: */ - if (this->flush(4U) == false) - { + if (this->flush(4U) == false) { return 0; // send timed out } uint32_t id; uint32_t id_type; - if (msg.isStandardId()) - { + if (msg.isStandardId()) { id = msg.getStandardId(); id_type = FDCAN_STANDARD_ID; - } - else if (msg.isExtendedId()) - { + } else if (msg.isExtendedId()) { id = msg.getExtendedId(); id_type = FDCAN_EXTENDED_ID; - } - else // error ? this shouldn't be possible + } else // error ? this shouldn't be possible { return 0; } - if (msg.data_length > 8) - { + if (msg.data_length > 8) { // invalid data length return 0; } @@ -278,55 +245,52 @@ int CAN::write(CanMsg const & msg) HAL_StatusTypeDef status; - #if defined(STM32H7) - // NOTE: on the H7 this is done in two steps: (1) place the message in the buffer, (2) activate the transmission request - // place the message in the first buffer +#if defined(STM32H7) + // NOTE: on the H7 this is done in two steps: (1) place the message in the buffer, (2) activate the transmission + // request place the message in the first buffer status = HAL_FDCAN_AddMessageToTxBuffer(&(this->hfdcan), &tx_header, msg.data, FDCAN_TX_BUFFER0); - if (status != HAL_OK) - { + if (status != HAL_OK) { return 0; } // enable transmission request status = HAL_FDCAN_EnableTxBufferRequest(&(this->hfdcan), FDCAN_TX_BUFFER0); - #elif defined(STM32G4) - // On the G4 the HAL function HAL_FDCAN_AddMessageToTxFifoQ() both adds the message to the buffer or queue and activates the transmission request +#elif defined(STM32G4) + // On the G4 the HAL function HAL_FDCAN_AddMessageToTxFifoQ() both adds the message to the buffer or queue and + // activates the transmission request status = HAL_FDCAN_AddMessageToTxFifoQ(&(this->hfdcan), &tx_header, msg.data); - #endif +#else +#error Error +#endif return (status == HAL_OK) ? 1 : 0; } - -CanMsg CAN::read(void) -{ - if (this->hfdcan.Instance == nullptr) - { +CanMsg CAN::read(void) { + if (this->hfdcan.Instance == nullptr) { return CanMsg(); } // check that there is room in the rx fifo size_t count = this->available(); - if (count == 0) - { + if (count == 0) { return CanMsg(); // return empty message } - + HAL_StatusTypeDef status = HAL_FDCAN_GetRxMessage(&(this->hfdcan), FDCAN_RX_FIFO0, &(this->rx_header), this->rx_data); - if (status != HAL_OK) - { + if (status != HAL_OK) { return CanMsg(); } uint32_t arduino_msg_id = this->rx_header.Identifier; - if (this->rx_header.IdType == FDCAN_EXTENDED_ID) - { - arduino_msg_id |= CanMsg::CAN_EFF_FLAG; // bit 31 marks it as extended ID format according to the comment in CanMsg.h + if (this->rx_header.IdType == FDCAN_EXTENDED_ID) { + arduino_msg_id |= + CanMsg::CAN_EFF_FLAG; // bit 31 marks it as extended ID format according to the comment in CanMsg.h } // For classic CAN messages, clamp payload length to 8 bytes maximum (DLC 9-15 indicate 8 bytes in CAN 2.0B) @@ -335,10 +299,8 @@ CanMsg CAN::read(void) return CanMsg(arduino_msg_id, len, this->rx_data); } -void CAN::end(void) -{ - if (this->hfdcan.Instance == nullptr) - { +void CAN::end(void) { + if (this->hfdcan.Instance == nullptr) { return; } @@ -348,33 +310,26 @@ void CAN::end(void) return; } - -void CAN::cmd_can_send_test(void) -{ +void CAN::cmd_can_send_test(void) { static int count = 0; - while (!CommsSerial.available()) - { - CanMsg to_send(0x00, 8, (const uint8_t*)"HELLO123"); + while (!CommsSerial.available()) { + CanMsg to_send(0x00, 8, (const uint8_t *)"HELLO123"); - if (this->write(to_send) != 1) - { + if (this->write(to_send) != 1) { CommsSerial.println("can.write() failed"); return; } uint32_t start_ms = millis(); - while (!this->available()) - { - if (millis() - start_ms > 500) - { + while (!this->available()) { + if (millis() - start_ms > 500) { CommsSerial.println("Error: reply timed out"); return; } } - while (this->available()) - { + while (this->available()) { CanMsg msg = this->read(); CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); @@ -384,34 +339,27 @@ void CAN::cmd_can_send_test(void) } } -void CAN::cmd_can_receive_test(void) -{ +void CAN::cmd_can_receive_test(void) { static int count = 0; - while (!CommsSerial.available()) - { + while (!CommsSerial.available()) { uint32_t start_ms = millis(); - while (!this->available()) - { - if (millis() - start_ms > 5000) - { + while (!this->available()) { + if (millis() - start_ms > 5000) { CommsSerial.println("Error: receive timed out"); return; } } - while (this->available()) - { + while (this->available()) { CanMsg msg = this->read(); CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); } + CanMsg to_send(0x00, 8, (const uint8_t *)"REPLY123"); - CanMsg to_send(0x00, 8, (const uint8_t*)"REPLY123"); - - if (this->write(to_send) != 1) - { + if (this->write(to_send) != 1) { CommsSerial.println("can.write() failed"); return; } diff --git a/firmware/lib/fdcan/fdcan_toad.h b/firmware/lib/fdcan/fdcan_toad.h index 1d23d42..f4b88e3 100644 --- a/firmware/lib/fdcan/fdcan_toad.h +++ b/firmware/lib/fdcan/fdcan_toad.h @@ -1,113 +1,98 @@ #pragma once -#include -#include "api/HardwareCAN.h" #include "Arduino.h" #include "CommandRouter.h" +#include "api/HardwareCAN.h" +#include #if defined(STM32H7) #include "stm32h7xx_hal.h" #elif defined(STM32G4) #include "stm32g474xx.h" +#else +#error Error #endif -class CAN; - -class CAN : public arduino::HardwareCAN -{ +class CAN : public arduino::HardwareCAN { public: - CAN(uint32_t tx_pin, uint32_t rx_pin); - - enum class error_type_t - { - TX_ERROR, - RX_ERROR, - RX_ERROR_PASSIVE - }; - - // Initialize using standard Arduino CanBitRate enum. - // Conforms to arduino::HardwareCAN interface. - // Returns true on success, false on failure. - bool begin(CanBitRate bit_rate) override; - - // Get number of messages waiting in the receive FIFO. - // Conforms to arduino::HardwareCAN interface. - size_t available(void) override; - - // Get number of free positions in the transmit buffer (1 if free, 0 if busy). - uint32_t tx_free_count(void); - - // Transmit a data frame in classic CAN mode with an 11-bit or 29-bit ID. - // Conforms to arduino::HardwareCAN interface. - // Returns 1 on success, 0 on error or timeout. - int write(arduino::CanMsg const & msg) override; - - // Retrieve the next received message from the receive FIFO. - // Conforms to arduino::HardwareCAN interface. - // Returns an empty CanMsg if no message was available or an error occurred. - arduino::CanMsg read(void) override; - - // Disable the FDCAN peripheral. - // Conforms to arduino::HardwareCAN interface. - void end(void) override; - - // Wait for ongoing transmission in TX buffer 0 to complete. - // Returns true if buffer is free, false if timed out. - bool flush(uint32_t timeout_ms = UINT32_MAX); - - /* Test sending data on one CAN, and expect to receive data after every message */ - void cmd_can_send_test(void); - - /* Test receiving data on one CAN, and send a frame after every received message */ - void cmd_can_receive_test(void); - - struct FDCAN_Handle_Wrapper : public FDCAN_HandleTypeDef - { - CAN* obj; // pointer back to the CAN object that owns this peripheral - }; - - - FDCAN_Handle_Wrapper* handle(void) - { - return &hfdcan; - } - + CAN(uint32_t tx_pin, uint32_t rx_pin); + + enum class error_type_t { TX_ERROR, RX_ERROR, RX_ERROR_PASSIVE }; + + // Initialize using standard Arduino CanBitRate enum. + // Conforms to arduino::HardwareCAN interface. + // Returns true on success, false on failure. + bool begin(CanBitRate bit_rate) override; + + // Get number of messages waiting in the receive FIFO. + // Conforms to arduino::HardwareCAN interface. + size_t available(void) override; + + // Get number of free positions in the transmit buffer (1 if free, 0 if busy). + uint32_t tx_free_count(void); + + // Transmit a data frame in classic CAN mode with an 11-bit or 29-bit ID. + // Conforms to arduino::HardwareCAN interface. + // Returns 1 on success, 0 on error or timeout. + int write(arduino::CanMsg const &msg) override; + + // Retrieve the next received message from the receive FIFO. + // Conforms to arduino::HardwareCAN interface. + // Returns an empty CanMsg if no message was available or an error occurred. + arduino::CanMsg read(void) override; + + // Disable the FDCAN peripheral. + // Conforms to arduino::HardwareCAN interface. + void end(void) override; + + // Wait for ongoing transmission in TX buffer 0 to complete. + // Returns true if buffer is free, false if timed out. + bool flush(uint32_t timeout_ms = UINT32_MAX); + + /* Test sending data on one CAN, and expect to receive data after every message */ + void cmd_can_send_test(void); + + /* Test receiving data on one CAN, and send a frame after every received message */ + void cmd_can_receive_test(void); + + struct FDCAN_Handle_Wrapper : public FDCAN_HandleTypeDef { + CAN *obj; // pointer back to the CAN object that owns this peripheral + }; + + FDCAN_Handle_Wrapper *handle(void) { + return &hfdcan; + } + private: - FDCAN_Handle_Wrapper hfdcan = {0}; - FDCAN_ErrorCountersTypeDef error_counts = {0}; - FDCAN_RxHeaderTypeDef rx_header = {0}; - uint32_t tx_pin = 0; - uint32_t rx_pin = 0; - uint8_t rx_data[64] = {0}; + FDCAN_Handle_Wrapper hfdcan = {0}; + FDCAN_ErrorCountersTypeDef error_counts = {0}; + FDCAN_RxHeaderTypeDef rx_header = {0}; + uint32_t tx_pin = 0; + uint32_t rx_pin = 0; + uint8_t rx_data[64] = {0}; }; - -template -void cmd_can_loopback(void) -{ +template void cmd_can_loopback(void) { // repeat until the user presses a button - while (!CommsSerial.available()) - { + while (!CommsSerial.available()) { unsigned long count = 0; CommsSerial.printf("Test %u:\n", count); - CanMsg test_msg(0x123, 8, (const uint8_t*)"HELLO123"); + CanMsg test_msg(0x123, 8, (const uint8_t *)"HELLO123"); sender.write(test_msg); uint32_t start_ms = millis(); - while (!receiver.available()) - { - if (millis() - start_ms > 500) - { + while (!receiver.available()) { + if (millis() - start_ms > 500) { CommsSerial.println("Receive timed out"); return; } } - while (receiver.available()) - { + while (receiver.available()) { CanMsg received = receiver.read(); - CommsSerial.printf("Received message: ID: 0x%04X\tDLC: %2d\tData: %.*s\n", received.id, received.data_length, received.data_length, (const char*)received.data); + CommsSerial.printf("Received message: ID: 0x%04X\tDLC: %2d\tData: %.*s\n", received.id, received.data_length, + received.data_length, (const char *)received.data); } delay(500); diff --git a/firmware/src/ec_main.cpp b/firmware/src/ec_main.cpp index 752f2d8..cccb7b9 100644 --- a/firmware/src/ec_main.cpp +++ b/firmware/src/ec_main.cpp @@ -50,7 +50,6 @@ void setup() { bool all_modules_ok = true; - all_modules_ok &= can_begin(); all_modules_ok &= RS485s::begin(); all_modules_ok &= PressureSensors::begin(); @@ -59,7 +58,6 @@ void setup() { all_modules_ok &= SolenoidValves::begin(); all_modules_ok &= TVC_Actuators::begin(); all_modules_ok &= ValveController::begin(); - if (!all_modules_ok) { while (true) { @@ -72,7 +70,6 @@ void setup() { CommandRouter::add(flight_loop, "start_flight_loop"); CommandRouter::add_flag(&kill_flag, "k", "terminate the flight loop early"); CommandRouter::add_flag(&arm_flag, "arm", "start following a trajectory"); - } void loop() { @@ -128,20 +125,19 @@ void flight_loop() { RCS::close(); } - -bool can_begin(void) -{ +bool can_begin(void) { bool is_ok = true; is_ok &= can_tvc.begin(CanBitRate::BR_500k); is_ok &= can_fc.begin(CanBitRate::BR_500k); - if (is_ok) - { + if (is_ok) { CommandRouter::add(cmd_can_loopback, "tvc_to_fc_test"); CommandRouter::add(cmd_can_loopback, "fc_to_tvc_test"); - CommandRouter::add([](){can_fc.cmd_can_send_test();}, "can_send_test"); - CommandRouter::add([](){can_tvc.cmd_can_receive_test();}, "can_receive_test"); + CommandRouter::add([]() { can_fc.cmd_can_send_test(); }, "can_fc_send_test"); + CommandRouter::add([]() { can_tvc.cmd_can_send_test(); }, "can_tvc_send_test"); + CommandRouter::add([]() { can_fc.cmd_can_receive_test(); }, "can_fc_receive_test"); + CommandRouter::add([]() { can_tvc.cmd_can_receive_test(); }, "can_tvc_receive_test"); } return is_ok; diff --git a/firmware/src/prog_main.cpp b/firmware/src/prog_main.cpp index c831901..a3e479e 100644 --- a/firmware/src/prog_main.cpp +++ b/firmware/src/prog_main.cpp @@ -1,292 +1,239 @@ -// #include - -// #include "CommsSerial.h" -// #include "prog_pins.h" -// #include "toad_can_bus.h" - -// // Based on -// // https://www.st.com/resource/en/application_note/an4286-how-to-use-spi-protocol-in-bootloader-on-stm32-mcus-stmicroelectronics.pdf -// // Citations commented as [pg#] - -// CommsSerial_t USB_CommsSerial; -// CommsSerial_t HW_CommsSerial(PIN_HW_COMM_SERIAL_RX, PIN_HW_COMM_SERIAL_TX); -// CommsSerial_t HW_FallbackSerial(PIN_HW_FALLBACK_SERIAL_RX, PIN_HW_FALLBACK_SERIAL_TX); - -// SPIClass Prog_SPI(PIN_PROG_SPI_MOSI, PIN_PROG_SPI_MISO, PIN_PROG_SPI_SCK); - -// constexpr uint8_t ACK = 0x79; -// constexpr uint8_t NACK = 0x15; -// constexpr uint8_t SYNC = 0x5A; -// constexpr uint8_t CMD_WriteMem = 0x31; -// constexpr uint8_t CMD_EraseMem = 0x44; - -// // If return value of function is false, return false from the outer function -// // TODO - not sure if this is the design pattern we want -// #define ExitOnFail(expr) \ -// do { \ -// if (!(expr)) \ -// return false; \ -// } while (0) - -// prog_id_t prog_type; - -// enum prog_state_t { STATE_IDLE, STATE_PRE_ERASE, STATE_READY, STATE_PAGE_SELECTED } prog_state; - -// // 2 MB of Flash divided into 512 x 4096 byte virtual pages. -// // Chunks for the active page are received 32 bytes at a time, for 128 chunks per page. -// // The active page is written out 256 bytes at a time. -// constexpr size_t CAN_CHUNK_SIZE = 32; // bytes -// constexpr size_t WRITE_CHUNK_SIZE = 256; // bytes; -// constexpr size_t PAGE_CACHE_SIZE = 4096; // bytes -// constexpr size_t NUM_CAN_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / CAN_CHUNK_SIZE; -// constexpr size_t NUM_WRITE_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / WRITE_CHUNK_SIZE; -// constexpr size_t NUM_PAGE_CACHES = 512; -// static_assert(PAGE_CACHE_SIZE * NUM_PAGE_CACHES == 2097152); - -// uint32_t active_pg_addr; -// uint8_t page_cache[PAGE_CACHE_SIZE]; -// bool chunk_rcv[NUM_CAN_CHUNKS_PER_PAGE]; - -// void setup() { -// // All shared interfaces are begun here. - -// // Use same baud rate on all Comm Serials for consistency. -// USB_CommsSerial.begin(RADIO_BAUD); -// HW_CommsSerial.begin(RADIO_BAUD); -// HW_FallbackSerial.begin(RADIO_BAUD); - -// // Configure SPI interface and set CS HIGH -// Prog_SPI.begin(); -// digitalWrite(PIN_PROG_SPI_CS, HIGH); - -// // Configure BOOT and NRST to default the STM32H7 into normal code execution -// digitalWrite(PIN_H7_BOOT, BOOT_MODE_RUN); -// pinMode(PIN_H7_BOOT, OUTPUT); - -// digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); -// pinMode(PIN_H7_NRST, OUTPUT); - -// pinMode(PIN_PROG_ID, INPUT); -// prog_type = (digitalRead(PIN_PROG_ID) == PROG_ID_FLIGHT_CONTROLLER) ? PROG_FLIGHT_CONTROLLER : PROG_ENGINE_CONTROLLER; - -// delay(3000); - -// if (prog_type == PROG_FLIGHT_CONTROLLER) { -// CommsSerial.println("Flight Controller Programmer Started!"); -// } else { -// CommsSerial.println("Engine Controller Programmer Started!"); -// } - -// prog_state = STATE_IDLE; -// } - -// void reset_h7() { -// digitalWrite(PIN_H7_NRST, NRST_MODE_RST); -// delay(1000); -// digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); -// } - -// // Sends ACK frame, returns true if bootloader -// // responded with ACK, false otherwise -// // TODO - pretty sure sometimes this has to cycle around until ACK -// bool spi_ack_frame() { -// Prog_SPI.transfer(0x00); -// uint8_t resp = Prog_SPI.transfer(0x00); -// Prog_SPI.transfer(ACK); - -// if (resp == ACK) { -// return true; -// } else if (resp == NACK) { -// return false; -// } else { -// return false; -// } -// } - -// bool enter_bootloader() { -// // Reset the H7 into the bootloader -// digitalWrite(PIN_H7_BOOT, BOOT_MODE_FLASH); -// reset_h7(); - -// // Send SYNC byte and get ACK [6] -// digitalWrite(PIN_PROG_SPI_CS, LOW); - -// Prog_SPI.transfer(SYNC); -// ExitOnFail(spi_ack_frame()); - -// return true; -// } - -// bool erase_memory() { -// Prog_SPI.transfer(SYNC); -// Prog_SPI.transfer(CMD_EraseMem); -// Prog_SPI.transfer(~CMD_EraseMem); -// ExitOnFail(spi_ack_frame()); - -// // Mass erase [26] -// Prog_SPI.transfer(0xFF); -// Prog_SPI.transfer(0xFF); -// Prog_SPI.transfer(0xFF ^ 0xFF); -// ExitOnFail(spi_ack_frame()); - -// return true; -// } - -// bool write_memory(uint32_t addr, const uint8_t *bytes, size_t len) { -// Prog_SPI.transfer(SYNC); -// Prog_SPI.transfer(CMD_WriteMem); -// Prog_SPI.transfer(~CMD_WriteMem); -// ExitOnFail(spi_ack_frame()); - -// uint8_t addr_byte_0 = (addr >> 24) & 0xFF; -// uint8_t addr_byte_1 = (addr >> 16) & 0xFF; -// uint8_t addr_byte_2 = (addr >> 8) & 0xFF; -// uint8_t addr_byte_3 = (addr >> 0) & 0xFF; - -// Prog_SPI.transfer(addr_byte_0); -// Prog_SPI.transfer(addr_byte_1); -// Prog_SPI.transfer(addr_byte_2); -// Prog_SPI.transfer(addr_byte_3); -// Prog_SPI.transfer(addr_byte_0 ^ addr_byte_1 ^ addr_byte_2 ^ addr_byte_3); -// ExitOnFail(spi_ack_frame()); - -// uint8_t chk_sum = (len - 1) & 0xFF; -// Prog_SPI.transfer((len - 1) & 0xFF); -// for (size_t i = 0; i < len; i++) { -// chk_sum ^= bytes[i]; -// Prog_SPI.transfer(bytes[i]); -// } -// Prog_SPI.transfer(chk_sum); -// ExitOnFail(spi_ack_frame()); - -// return true; -// } - -// void loop() { -// // TODO - prog state machine -// // Wait for CAN cmds, control reset and boot -// // Load flash files and flash over SPI - -// // can.pump_events() or similar -// // process commands - note that each command must be guaranteed to not halt -// // TODO - figure out how to parse the incoming cmd and payload -// uint8_t *raw_bytes; -// size_t raw_msg_len; - -// CAN_Msg_Decoder raw_msg(raw_bytes, raw_msg_len, prog_state); - -// if (const auto msg = raw_msg.decode()) { -// // TODO - this - -// } else if (const auto msg = raw_msg.decode()) { -// // TODO - enforce state -// reset_h7(); - -// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_IDLE)) { -// if (enter_bootloader()) { -// prog_state = STATE_PRE_ERASE; -// } else { -// // TODO - handle error -// } - -// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PRE_ERASE)) { -// if (erase_memory()) { -// prog_state = STATE_READY; -// } else { -// // TODO - handle error -// } - -// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_READY)) { -// prog_state = STATE_PAGE_SELECTED; -// active_pg_addr = msg->page_addr; -// for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { -// chunk_rcv[i] = false; -// } - -// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { -// if (msg->page_addr == active_pg_addr) { -// static_assert(sizeof(msg->flash_bytes) == CAN_CHUNK_SIZE); -// memcpy(&page_cache[msg->chunk_addr * CAN_CHUNK_SIZE], msg->flash_bytes, CAN_CHUNK_SIZE); -// chunk_rcv[msg->chunk_addr] = true; -// } else { -// // TODO - handle wrong page addr -// } - -// } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { -// bool all_chunk_rcv = true; -// for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { -// if (!chunk_rcv) { -// can_msg_request_mem_packet_t resp; -// resp.chunk_addr = i; -// resp.page_addr = active_pg_addr; -// // TODO - send request message -// all_chunk_rcv = false; -// } -// } - -// if (all_chunk_rcv) { -// for (size_t i = 0; i < NUM_WRITE_CHUNKS_PER_PAGE; i++) { -// static_assert(WRITE_CHUNK_SIZE <= 256); -// write_memory(active_pg_addr * PAGE_CACHE_SIZE + WRITE_CHUNK_SIZE * i, &page_cache[WRITE_CHUNK_SIZE * i], -// WRITE_CHUNK_SIZE); -// // TODO - send ok -// } -// prog_state = STATE_READY; -// } -// } - -// raw_msg.send_error_if_not_decoded(); -// } - -// #include "CommsSerial.h" #include -#include "prog_pins.h" -#include "CommsSerial.h" - -#define RADIO_BAUD 57600 +#include "CommsSerial.h" +#include "prog_pins.h" +#include "toad_can_bus.h" -CAN can_gse(PIN_CAN_GSE_TX, PIN_CAN_GSE_RX); +// Based on +// https://www.st.com/resource/en/application_note/an4286-how-to-use-spi-protocol-in-bootloader-on-stm32-mcus-stmicroelectronics.pdf +// Citations commented as [pg#] CommsSerial_t USB_CommsSerial; CommsSerial_t HW_CommsSerial(PIN_HW_COMM_SERIAL_RX, PIN_HW_COMM_SERIAL_TX); CommsSerial_t HW_FallbackSerial(PIN_HW_FALLBACK_SERIAL_RX, PIN_HW_FALLBACK_SERIAL_TX); +SPIClass Prog_SPI(PIN_PROG_SPI_MOSI, PIN_PROG_SPI_MISO, PIN_PROG_SPI_SCK); + +constexpr uint8_t ACK = 0x79; +constexpr uint8_t NACK = 0x15; +constexpr uint8_t SYNC = 0x5A; +constexpr uint8_t CMD_WriteMem = 0x31; +constexpr uint8_t CMD_EraseMem = 0x44; + +// If return value of function is false, return false from the outer function +// TODO - not sure if this is the design pattern we want +#define ExitOnFail(expr) \ + do { \ + if (!(expr)) \ + return false; \ + } while (0) + +prog_id_t prog_type; + +enum prog_state_t { STATE_IDLE, STATE_PRE_ERASE, STATE_READY, STATE_PAGE_SELECTED } prog_state; + +// 2 MB of Flash divided into 512 x 4096 byte virtual pages. +// Chunks for the active page are received 32 bytes at a time, for 128 chunks per page. +// The active page is written out 256 bytes at a time. +constexpr size_t CAN_CHUNK_SIZE = 32; // bytes +constexpr size_t WRITE_CHUNK_SIZE = 256; // bytes; +constexpr size_t PAGE_CACHE_SIZE = 4096; // bytes +constexpr size_t NUM_CAN_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / CAN_CHUNK_SIZE; +constexpr size_t NUM_WRITE_CHUNKS_PER_PAGE = PAGE_CACHE_SIZE / WRITE_CHUNK_SIZE; +constexpr size_t NUM_PAGE_CACHES = 512; +static_assert(PAGE_CACHE_SIZE * NUM_PAGE_CACHES == 2097152); + +uint32_t active_pg_addr; +uint8_t page_cache[PAGE_CACHE_SIZE]; +bool chunk_rcv[NUM_CAN_CHUNKS_PER_PAGE]; + void setup() { + // All shared interfaces are begun here. + // Use same baud rate on all Comm Serials for consistency. USB_CommsSerial.begin(RADIO_BAUD); HW_CommsSerial.begin(RADIO_BAUD); HW_FallbackSerial.begin(RADIO_BAUD); - if (!can_gse.begin(CanBitRate::BR_500k)) - { - for (;;) - { - CommsSerial.println("CAN failed to begin()"); + // Configure SPI interface and set CS HIGH + Prog_SPI.begin(); + digitalWrite(PIN_PROG_SPI_CS, HIGH); - delay(1000); - } + // Configure BOOT and NRST to default the STM32H7 into normal code execution + digitalWrite(PIN_H7_BOOT, BOOT_MODE_RUN); + pinMode(PIN_H7_BOOT, OUTPUT); + + digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); + pinMode(PIN_H7_NRST, OUTPUT); + + pinMode(PIN_PROG_ID, INPUT); + prog_type = (digitalRead(PIN_PROG_ID) == PROG_ID_FLIGHT_CONTROLLER) ? PROG_FLIGHT_CONTROLLER : PROG_ENGINE_CONTROLLER; + + delay(3000); + + if (prog_type == PROG_FLIGHT_CONTROLLER) { + CommsSerial.println("Flight Controller Programmer Started!"); + } else { + CommsSerial.println("Engine Controller Programmer Started!"); + } + + prog_state = STATE_IDLE; +} + +void reset_h7() { + digitalWrite(PIN_H7_NRST, NRST_MODE_RST); + delay(1000); + digitalWrite(PIN_H7_NRST, NRST_MODE_RUN); +} + +// Sends ACK frame, returns true if bootloader +// responded with ACK, false otherwise +// TODO - pretty sure sometimes this has to cycle around until ACK +bool spi_ack_frame() { + Prog_SPI.transfer(0x00); + uint8_t resp = Prog_SPI.transfer(0x00); + Prog_SPI.transfer(ACK); + + if (resp == ACK) { + return true; + } else if (resp == NACK) { + return false; + } else { + return false; + } +} + +bool enter_bootloader() { + // Reset the H7 into the bootloader + digitalWrite(PIN_H7_BOOT, BOOT_MODE_FLASH); + reset_h7(); + + // Send SYNC byte and get ACK [6] + digitalWrite(PIN_PROG_SPI_CS, LOW); + + Prog_SPI.transfer(SYNC); + ExitOnFail(spi_ack_frame()); + + return true; +} + +bool erase_memory() { + Prog_SPI.transfer(SYNC); + Prog_SPI.transfer(CMD_EraseMem); + Prog_SPI.transfer(~CMD_EraseMem); + ExitOnFail(spi_ack_frame()); + + // Mass erase [26] + Prog_SPI.transfer(0xFF); + Prog_SPI.transfer(0xFF); + Prog_SPI.transfer(0xFF ^ 0xFF); + ExitOnFail(spi_ack_frame()); + + return true; +} + +bool write_memory(uint32_t addr, const uint8_t *bytes, size_t len) { + Prog_SPI.transfer(SYNC); + Prog_SPI.transfer(CMD_WriteMem); + Prog_SPI.transfer(~CMD_WriteMem); + ExitOnFail(spi_ack_frame()); + + uint8_t addr_byte_0 = (addr >> 24) & 0xFF; + uint8_t addr_byte_1 = (addr >> 16) & 0xFF; + uint8_t addr_byte_2 = (addr >> 8) & 0xFF; + uint8_t addr_byte_3 = (addr >> 0) & 0xFF; + + Prog_SPI.transfer(addr_byte_0); + Prog_SPI.transfer(addr_byte_1); + Prog_SPI.transfer(addr_byte_2); + Prog_SPI.transfer(addr_byte_3); + Prog_SPI.transfer(addr_byte_0 ^ addr_byte_1 ^ addr_byte_2 ^ addr_byte_3); + ExitOnFail(spi_ack_frame()); + + uint8_t chk_sum = (len - 1) & 0xFF; + Prog_SPI.transfer((len - 1) & 0xFF); + for (size_t i = 0; i < len; i++) { + chk_sum ^= bytes[i]; + Prog_SPI.transfer(bytes[i]); } + Prog_SPI.transfer(chk_sum); + ExitOnFail(spi_ack_frame()); - CommsSerial.println("init"); // had to add this; for some reason without it we don't pull in Print.cpp which causes a - // linker error because _write is not defined + return true; } -void loop() -{ - static int count = 0; - if (can_gse.available()) - { - // receive and print out message - CanMsg msg = can_gse.read(); +void loop() { + // TODO - prog state machine + // Wait for CAN cmds, control reset and boot + // Load flash files and flash over SPI + + // can.pump_events() or similar + // process commands - note that each command must be guaranteed to not halt + // TODO - figure out how to parse the incoming cmd and payload + uint8_t *raw_bytes; + size_t raw_msg_len; - CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); + CAN_Msg_Decoder raw_msg(raw_bytes, raw_msg_len, prog_state); - // send a reply - CanMsg reply(0x01, 8, (const uint8_t*)"PROG->EC"); + if (const auto msg = raw_msg.decode()) { + // TODO - this - can_gse.write(reply); + } else if (const auto msg = raw_msg.decode()) { + // TODO - enforce state + reset_h7(); + + } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_IDLE)) { + if (enter_bootloader()) { + prog_state = STATE_PRE_ERASE; + } else { + // TODO - handle error + } + + } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PRE_ERASE)) { + if (erase_memory()) { + prog_state = STATE_READY; + } else { + // TODO - handle error + } + + } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_READY)) { + prog_state = STATE_PAGE_SELECTED; + active_pg_addr = msg->page_addr; + for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { + chunk_rcv[i] = false; + } + + } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { + if (msg->page_addr == active_pg_addr) { + static_assert(sizeof(msg->flash_bytes) == CAN_CHUNK_SIZE); + memcpy(&page_cache[msg->chunk_addr * CAN_CHUNK_SIZE], msg->flash_bytes, CAN_CHUNK_SIZE); + chunk_rcv[msg->chunk_addr] = true; + } else { + // TODO - handle wrong page addr + } + + } else if (const auto msg = raw_msg.decode_and_enforce_state(STATE_PAGE_SELECTED)) { + bool all_chunk_rcv = true; + for (size_t i = 0; i < NUM_CAN_CHUNKS_PER_PAGE; i++) { + if (!chunk_rcv) { + can_msg_request_mem_packet_t resp; + resp.chunk_addr = i; + resp.page_addr = active_pg_addr; + // TODO - send request message + all_chunk_rcv = false; + } + } + + if (all_chunk_rcv) { + for (size_t i = 0; i < NUM_WRITE_CHUNKS_PER_PAGE; i++) { + static_assert(WRITE_CHUNK_SIZE <= 256); + write_memory(active_pg_addr * PAGE_CACHE_SIZE + WRITE_CHUNK_SIZE * i, &page_cache[WRITE_CHUNK_SIZE * i], + WRITE_CHUNK_SIZE); + // TODO - send ok + } + prog_state = STATE_READY; + } } - delay(50); + raw_msg.send_error_if_not_decoded(); } \ No newline at end of file From 9c56149928eeadabd6887d49021b53e48a3e9b27 Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Wed, 4 Nov 2026 13:02:53 -0500 Subject: [PATCH 8/9] a few comments, get rid of unused symbols, comment SystemClock_Config for G4 to address PR comments --- firmware/boards/TOAD_G4/variant_TOAD_G4.cpp | 45 ++++++++++++--------- firmware/lib/fdcan/fdcan_toad.cpp | 1 - firmware/lib/fdcan/fdcan_toad.h | 15 ++----- firmware/src/ec_main.cpp | 2 + 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp index fdffd50..e98e8ef 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp @@ -106,26 +106,30 @@ const uint32_t analogInputPin[] = { void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {}; - RCC_CRSInitTypeDef pInit = {}; + RCC_CRSInitTypeDef CRS_Init = {}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {}; /** Configure the main internal regulator output voltage */ + /* */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ + + /* See RM[275] for clock tree */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 | RCC_OSCILLATORTYPE_HSE; - RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; + RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; /* Our HSE is 8MHz; we use a MEMS oscillator instead of crystal + oscillator, hence BYPASS (RM[277]) */ RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2; - RCC_OscInitStruct.PLL.PLLN = 60; - RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; - RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; - RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; /* PLL Source is HSE */ + RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2; /* Prescaler; scales down to 4MHz */ + RCC_OscInitStruct.PLL.PLLN = 60; /* Multiplier; scales up to 240MHz */ + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; /* PLLPCLK = 120MHz */ + RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; /* PLLQCLK = 120MHz */ + RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; /* PLLRCLK = 120MHz */ if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } @@ -133,11 +137,12 @@ void SystemClock_Config(void) { /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; - RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; - RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; - RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; - RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; /* SYSCLK = PLLRCLK = 120MHz */ + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; /* AHB = 120MHz */ + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; /* APB1 = AHB = 120MHz */ + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; /* APB2 = AHB = 120MHz */ + /* Note: from RM[75] stm32g474 is a category 3 device */ if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) { Error_Handler(); } @@ -148,14 +153,14 @@ void SystemClock_Config(void) { /** Configures CRS */ - pInit.Prescaler = RCC_CRS_SYNC_DIV1; - pInit.Source = RCC_CRS_SYNC_SOURCE_USB; - pInit.Polarity = RCC_CRS_SYNC_POLARITY_RISING; - pInit.ReloadValue = __HAL_RCC_CRS_RELOADVALUE_CALCULATE(48000000, 1000); - pInit.ErrorLimitValue = 34; - pInit.HSI48CalibrationValue = 32; - - HAL_RCCEx_CRSConfig(&pInit); + CRS_Init.Prescaler = RCC_CRS_SYNC_DIV1; + CRS_Init.Source = RCC_CRS_SYNC_SOURCE_USB; + CRS_Init.Polarity = RCC_CRS_SYNC_POLARITY_RISING; + CRS_Init.ReloadValue = __HAL_RCC_CRS_RELOADVALUE_CALCULATE(48000000, 1000); + CRS_Init.ErrorLimitValue = 34; + CRS_Init.HSI48CalibrationValue = 32; + + HAL_RCCEx_CRSConfig(&CRS_Init); PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB; PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_HSI48; diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index d2a1f2d..90a35b6 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -19,7 +19,6 @@ CAN::CAN(uint32_t _tx_pin, uint32_t _rx_pin) { this->tx_pin = _tx_pin; this->rx_pin = _rx_pin; this->hfdcan = {0}; - this->hfdcan.obj = this; } // jhillman adapted from diff --git a/firmware/lib/fdcan/fdcan_toad.h b/firmware/lib/fdcan/fdcan_toad.h index f4b88e3..68f3c00 100644 --- a/firmware/lib/fdcan/fdcan_toad.h +++ b/firmware/lib/fdcan/fdcan_toad.h @@ -54,20 +54,13 @@ class CAN : public arduino::HardwareCAN { /* Test receiving data on one CAN, and send a frame after every received message */ void cmd_can_receive_test(void); - struct FDCAN_Handle_Wrapper : public FDCAN_HandleTypeDef { - CAN *obj; // pointer back to the CAN object that owns this peripheral - }; - - FDCAN_Handle_Wrapper *handle(void) { - return &hfdcan; - } - private: - FDCAN_Handle_Wrapper hfdcan = {0}; - FDCAN_ErrorCountersTypeDef error_counts = {0}; - FDCAN_RxHeaderTypeDef rx_header = {0}; + FDCAN_HandleTypeDef hfdcan = {0}; uint32_t tx_pin = 0; uint32_t rx_pin = 0; + + /* Used by CAN::read(); stores data here about the incoming packet without large stack frame */ + FDCAN_RxHeaderTypeDef rx_header = {0}; uint8_t rx_data[64] = {0}; }; diff --git a/firmware/src/ec_main.cpp b/firmware/src/ec_main.cpp index cccb7b9..8eabb1e 100644 --- a/firmware/src/ec_main.cpp +++ b/firmware/src/ec_main.cpp @@ -125,6 +125,8 @@ void flight_loop() { RCS::close(); } +/* Platform-dependent can_begin(); because fdcan lib is used by multiple platforms, it's best to keep platform-dependent + * init here */ bool can_begin(void) { bool is_ok = true; From 2e8f396b33fcfa40d577211c367586447188bddf Mon Sep 17 00:00:00 2001 From: jacobh460 Date: Wed, 4 Nov 2026 13:13:50 -0500 Subject: [PATCH 9/9] get rid of unnecessary members --- firmware/lib/fdcan/fdcan_toad.cpp | 13 ++++++++----- firmware/lib/fdcan/fdcan_toad.h | 4 ---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index 90a35b6..5ecd62d 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -268,6 +268,9 @@ int CAN::write(CanMsg const &msg) { } CanMsg CAN::read(void) { + FDCAN_RxHeaderTypeDef rx_header = {0}; + uint8_t rx_data[64] = {0}; + if (this->hfdcan.Instance == nullptr) { return CanMsg(); } @@ -279,23 +282,23 @@ CanMsg CAN::read(void) { return CanMsg(); // return empty message } - HAL_StatusTypeDef status = HAL_FDCAN_GetRxMessage(&(this->hfdcan), FDCAN_RX_FIFO0, &(this->rx_header), this->rx_data); + HAL_StatusTypeDef status = HAL_FDCAN_GetRxMessage(&(this->hfdcan), FDCAN_RX_FIFO0, &(rx_header), rx_data); if (status != HAL_OK) { return CanMsg(); } - uint32_t arduino_msg_id = this->rx_header.Identifier; + uint32_t arduino_msg_id = rx_header.Identifier; - if (this->rx_header.IdType == FDCAN_EXTENDED_ID) { + if (rx_header.IdType == FDCAN_EXTENDED_ID) { arduino_msg_id |= CanMsg::CAN_EFF_FLAG; // bit 31 marks it as extended ID format according to the comment in CanMsg.h } // For classic CAN messages, clamp payload length to 8 bytes maximum (DLC 9-15 indicate 8 bytes in CAN 2.0B) - uint8_t len = (this->rx_header.DataLength > FDCAN_DLC_BYTES_8) ? 8 : (uint8_t)this->rx_header.DataLength; + uint8_t len = (rx_header.DataLength > FDCAN_DLC_BYTES_8) ? 8 : (uint8_t)rx_header.DataLength; - return CanMsg(arduino_msg_id, len, this->rx_data); + return CanMsg(arduino_msg_id, len, rx_data); } void CAN::end(void) { diff --git a/firmware/lib/fdcan/fdcan_toad.h b/firmware/lib/fdcan/fdcan_toad.h index 68f3c00..fd660fe 100644 --- a/firmware/lib/fdcan/fdcan_toad.h +++ b/firmware/lib/fdcan/fdcan_toad.h @@ -58,10 +58,6 @@ class CAN : public arduino::HardwareCAN { FDCAN_HandleTypeDef hfdcan = {0}; uint32_t tx_pin = 0; uint32_t rx_pin = 0; - - /* Used by CAN::read(); stores data here about the incoming packet without large stack frame */ - FDCAN_RxHeaderTypeDef rx_header = {0}; - uint8_t rx_data[64] = {0}; }; template void cmd_can_loopback(void) {