diff --git a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp index 9efc9ce..e98e8ef 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.cpp @@ -103,47 +103,83 @@ const uint32_t analogInputPin[] = { * @param None * @retval None */ -WEAK void SystemClock_Config(void) { +void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {}; + RCC_CRSInitTypeDef CRS_Init = {}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {}; /** 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; + + /* See RM[275] for clock tree */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 | RCC_OSCILLATORTYPE_HSE; + 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_HSI; - RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4; - RCC_OscInitStruct.PLL.PLLN = 85; - RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; - RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV4; - 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(); } + /** 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 */ - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { + /* Note: from RM[75] stm32g474 is a category 3 device */ + 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(); + + /** Configures CRS + */ + 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; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } + + /* Peripheral clock enable */ + __HAL_RCC_USB_CLK_ENABLE(); + + /* 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 0d8808b..24e8469 100644 --- a/firmware/boards/TOAD_G4/variant_TOAD_G4.h +++ b/firmware/boards/TOAD_G4/variant_TOAD_G4.h @@ -203,6 +203,12 @@ #define HAL_QSPI_MODULE_ENABLED #endif +#if !defined(HAL_FDCAN_MODULE_DISABLED) // enable FDCAN HAL definitions +#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/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 ffe9e07..95cf283 100644 --- a/firmware/boards/TOAD_H7/variant_TOAD_H7.h +++ b/firmware/boards/TOAD_H7/variant_TOAD_H7.h @@ -296,13 +296,9 @@ #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 (USE_HAL_FDCAN_REGISTER_CALLBACKS) -#define USE_HAL_FDCAN_REGISTER_CALLBACKS 1 -#endif - #if !defined(HSE_VALUE) // our HSE is 8MHz diff --git a/firmware/lib/fdcan/fdcan_toad.cpp b/firmware/lib/fdcan/fdcan_toad.cpp index 18eed69..5ecd62d 100644 --- a/firmware/lib/fdcan/fdcan_toad.cpp +++ b/firmware/lib/fdcan/fdcan_toad.cpp @@ -2,34 +2,12 @@ #include "Arduino.h" -#include "stm32h7xx_hal.h" -#include "variant_TOAD_H7.h" -#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) - { - 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[]) -{ +static void CAN_init_gpio_dynamic(uint32_t pin, const PinMap pin_map[]) { PinName pin_name = digitalPinToPinName(pin); @@ -37,77 +15,30 @@ 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) -{ +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; @@ -115,15 +46,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 @@ -145,22 +75,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; } @@ -176,9 +102,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.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.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.RxFifo0ElmtsNbr = 8; this->hfdcan.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8; this->hfdcan.Init.RxFifo1ElmtsNbr = 0; @@ -186,11 +123,8 @@ 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 @@ -198,8 +132,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; @@ -208,42 +141,15 @@ 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 // 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) - { - 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) - { + /* 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; } @@ -251,8 +157,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; } @@ -263,41 +168,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; } @@ -305,41 +200,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; } @@ -355,59 +242,67 @@ 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; - // place the message in the first buffer - HAL_StatusTypeDef status = HAL_FDCAN_AddMessageToTxBuffer(&(this->hfdcan), &tx_header, msg.data, FDCAN_TX_BUFFER0); + HAL_StatusTypeDef status; - if (status != HAL_OK) - { +#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) { 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 + status = HAL_FDCAN_AddMessageToTxFifoQ(&(this->hfdcan), &tx_header, msg.data); +#else +#error Error +#endif + return (status == HAL_OK) ? 1 : 0; } +CanMsg CAN::read(void) { + FDCAN_RxHeaderTypeDef rx_header = {0}; + uint8_t rx_data[64] = {0}; -CanMsg CAN::read(void) -{ - if (this->hfdcan.Instance == nullptr) - { + 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) - { + 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) - { - arduino_msg_id |= CanMsg::CAN_EFF_FLAG; // bit 31 marks it as extended ID format according to the comment in CanMsg.h + 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) -{ - if (this->hfdcan.Instance == nullptr) - { +void CAN::end(void) { + if (this->hfdcan.Instance == nullptr) { return; } @@ -417,46 +312,60 @@ 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; -} +void CAN::cmd_can_send_test(void) { + static int count = 0; + while (!CommsSerial.available()) { + CanMsg to_send(0x00, 8, (const uint8_t *)"HELLO123"); -// note: must only be called from an interrupt context, from a single isr -void CAN::error_update_from_isr(void) -{ - if (this->hfdcan.Instance == nullptr) - { - return; - } + if (this->write(to_send) != 1) { + CommsSerial.println("can.write() failed"); + return; + } - FDCAN_ErrorCountersTypeDef new_error_counts = {0}; + uint32_t start_ms = millis(); - HAL_FDCAN_GetErrorCounters(&(this->hfdcan), &new_error_counts); + while (!this->available()) { + if (millis() - start_ms > 500) { + CommsSerial.println("Error: reply timed out"); + return; + } + } - if (this->error_cbk == nullptr) - { - memcpy(&(this->error_counts), &new_error_counts, sizeof(new_error_counts)); - return; - } + while (this->available()) { + CanMsg msg = this->read(); - if (new_error_counts.RxErrorCnt != this->error_counts.RxErrorCnt) - { - this->error_cbk(CAN::error_type_t::RX_ERROR, new_error_counts.RxErrorCnt); - } + CommsSerial.printf("(%3d) Received message (0x%08X): %.*s\n", count++, msg.id, msg.data_length, msg.data); + } - if (new_error_counts.RxErrorPassive != this->error_counts.RxErrorPassive) - { - this->error_cbk(CAN::error_type_t::RX_ERROR_PASSIVE, new_error_counts.RxErrorPassive); + delay(1000); } +} - if (new_error_counts.TxErrorCnt != this->error_counts.TxErrorCnt) - { - this->error_cbk(CAN::error_type_t::TX_ERROR, new_error_counts.TxErrorCnt); - } +void CAN::cmd_can_receive_test(void) { + static int count = 0; + while (!CommsSerial.available()) { + 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 5401971..fd660fe 100644 --- a/firmware/lib/fdcan/fdcan_toad.h +++ b/firmware/lib/fdcan/fdcan_toad.h @@ -1,109 +1,90 @@ #pragma once -#include -#include "api/HardwareCAN.h" #include "Arduino.h" -#include "stm32h7xx_hal.h" #include "CommandRouter.h" +#include "api/HardwareCAN.h" +#include -class CAN; - -// instantiated in fdcan_toad.cpp -extern CAN can_tvc; -extern CAN can_fc; +#if defined(STM32H7) +#include "stm32h7xx_hal.h" +#elif defined(STM32G4) +#include "stm32g474xx.h" +#else +#error Error +#endif -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 - }; - - 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. - 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; - - // 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); - - 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); - 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; - } + 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); - 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}; - CAN_error_cbk_t error_cbk = nullptr; + FDCAN_HandleTypeDef hfdcan = {0}; + uint32_t tx_pin = 0; + uint32_t rx_pin = 0; }; + +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/lib/hardware_mapping/ec_pins.h b/firmware/lib/hardware_mapping/ec_pins.h index 22f806e..0607edc 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,9 @@ #define PIN_CAN_FC_RX PH14 #define PIN_CAN_FC_TX PH13 +extern CAN can_tvc; +extern CAN 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..f5d2e42 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,8 @@ #define PIN_CAN_GSE_RX PB12 #define PIN_CAN_GSE_TX PB13 +extern CAN can_gse; + // ID Pin #define PIN_PROG_ID PB1 #define PROG_ID_FLIGHT_CONTROLLER HIGH 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/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..8eabb1e 100644 --- a/firmware/src/ec_main.cpp +++ b/firmware/src/ec_main.cpp @@ -21,9 +21,13 @@ 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(); +bool can_begin(void); void setup() { // All shared interfaces are begun here. @@ -46,10 +50,10 @@ void setup() { bool all_modules_ok = true; - all_modules_ok &= CAN::init(); + all_modules_ok &= can_begin(); + all_modules_ok &= RS485s::begin(); 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(); @@ -120,3 +124,23 @@ void flight_loop() { ThrottleValves::stop(); 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; + + 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_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; +} \ No newline at end of file