0
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
extern  uart_buffer x;
extern int looped;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      if(looped == 1){
          SET_BIT(USART2->CR1, USART_CR1_TXEIE);
      }

  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  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_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;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */
  SET_BIT(USART2->CR1, USART_CR1_RXNEIE);

  HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(USART2_IRQn);
  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32g4xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32g4xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

 uart_buffer x = {{0},0,0};
int looped = 0;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart2;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M4 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Prefetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32G4xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32g4xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26.
  */
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  if(((USART2->ISR & USART_ISR_RXNE) != 0) && ((USART2->CR1 & USART_CR1_RXNEIE) != 0)){
      x.buffer[x.headpointer++] = USART2->RDR;

      if(x.headpointer == buffersize ){
          looped = 1;
      }
  }

  if(((USART2->ISR & USART_ISR_TXE) != 0) && ((USART2->CR1 & USART_CR1_TXEIE) != 0)){

        while (x.headpointer != x.tailpointer) {
            x.headpointer = 0;
            looped = 0;
            USART2->TDR = x.buffer[x.tailpointer++];
            while((USART2->ISR & USART_ISR_TXE) == 0){}

            if (x.tailpointer == buffersize) {
                x.tailpointer = 0;


                CLEAR_BIT(USART2->CR1, USART_CR1_TXEIE);
            }
        }

        /*if (x.headpointer == x.tailpointer) {
            x.buffer[x.headpointer++] = '\0';
            CLEAR_BIT(USART2->CR1, USART_CR1_TXEIE);
        } */


  }
  /* USER CODE END USART2_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32g4xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/
#define USART2_TX_Pin GPIO_PIN_2
#define USART2_TX_GPIO_Port GPIOA
#define USART2_RX_Pin GPIO_PIN_3
#define USART2_RX_GPIO_Port GPIOA
#define T_SWDIO_Pin GPIO_PIN_13
#define T_SWDIO_GPIO_Port GPIOA
#define T_SWCLK_Pin GPIO_PIN_14
#define T_SWCLK_GPIO_Port GPIOA
#define T_SWO_Pin GPIO_PIN_3
#define T_SWO_GPIO_Port GPIOB
#define LD2_Pin GPIO_PIN_8
#define LD2_GPIO_Port GPIOB

/* USER CODE BEGIN Private defines */
#define buffersize 8

typedef struct uart_buffer{
    char buffer[buffersize];
    int headpointer;
    int tailpointer;
}uart_buffer;


/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

What i am trying to do is, everytime 8 letters is entered via user, buffer has circled back so it resets and the inputs appear on the screen. I defined a "looped" variable, whenever headpointer reaches buffersize, looped becomes 1, then we set the TXEIE 1 and start transmitting the data to screen, also setting the "looped" to 0.

Now i have checked it via debug menu. First 8 letters come to the screen no problem. Second 8 letters also come. But after inputting the second 8 letters, i started to use step into button to go 1 by 1. Looped becomes 1. TXEIE is set. We enter into transmit interrupt and looped becomes 0. We transmit all letters and clear TXEIE.

Then we jump back into "if(looped == 1)" in main.c. That line is highlighted and in debug menu i can see looped == 0. But then program enters into the function anyway and sets TXEIE. This doesnt happen inifnitely many times. Just once. Third 8 letters, Fourth 8 letters and so on, this behaviour only happens once for each group. Help is appreciated.

Optimization is set to none but i tried putting volatile on "looped" and "x" regardless but it didnt help

The Blitz
  • 3
  • 2
  • That's quite a complicated setup, but mixing using the HAL interrupt handler and your own code is rarely a good idea. The HAL handler will read characters / clear flags etc. Then your code tries to do the same. Either don't call the HAL at all, or use the HAL's callback functions. – pmacfarlane Sep 01 '23 at 07:54
  • @pmacfarlane i just copied the whole thing but its actually not that long. a condition check in main.c, few definitions in main.h and main code under "USART2_IRQHandler". I am actually trying to use usart interrupt to do this circular buffer for exercise. Any other advice? – The Blitz Sep 01 '23 at 08:11
  • Stepping through communications code is always dangerous and/or unreliable. Data takes time to be physically shifted out on the line. The line of code is executed, but it will execute 50 (or however many) more lines of code by the time a byte you "sent" is physically sent and flags are set. Debugging it via stepping through code is really hard unless you are very careful about where you can use a breakpoint and where you can't, otherwise it will just disrupt all the communication timings and flag updates. Also, you don't need != 0 check. If(var) is exactly that. Any non-zero evaluates to true. – Ilya Sep 01 '23 at 08:49
  • @Ilya ""while((USART2->ISR & USART_ISR_TXE) == 0){} "" i wrote this line so nothing happens during transmitting process. After i set "looped" to 0, theres 8 character transfers and that line ensures nothing happens during those 8 transfers. So looped is definitely 0 by the time we return back to main.c but it still enters if(looped == 1) anyway. (Also thanks for the != thing, didnt think about that but i guess its more readable this way) – The Blitz Sep 01 '23 at 09:10
  • while((USART2->ISR & USART_ISR_TXE) == 0) this does NOT block current thread until the end of transmission process. UART has another register inside it (inaccessible to you), from which it sends stuff. It's usually referred to in docs as shift register or shiftout register. This only means that you can load the next byte into transmit register, but data from shift register may still be shifting out during this time. So it's not as simple. Empty transmit register does not mean that the data has already been physically shifted out. It's a bit of a mess because of that too. – Ilya Sep 01 '23 at 09:20
  • @Ilya I see, do you have any idea how i could resolve my issue then? Is my problem due to me trying to use "looped" in two different files? (main.c and it.c are different files right?) Should i somehow carry the stuff in "USART2_IRQHandler to main? – The Blitz Sep 01 '23 at 09:34
  • I think your head and tail pointers do shady stuff. It's really hard to follow the logic of what they do. Plus, you can always wrap around head and tail with modulo. Also, having a while() inside IRQ handler to send data defeats the whole purpose of interrupt implementation and blocks other UART interrupts from execution. You have a blocking UART implementation that also uses interrupt handler. You should take a step back and reogranize the logic using pretty good buffer structure that you already have there. Interrupt handler needs to check what UART is doing now, load/read one byte and exit. – Ilya Sep 01 '23 at 09:59
  • _But then program enters into the function anyway_ - what function? Single-step in disasm to see what happens under the hood. Also, do data arrive to Rx during the single-stepping process you've described? Also, you mention that the observed deviation happens only once per 8-byte data bunch - where exactly within that bunch? – wek Sep 01 '23 at 10:01
  • @wek (what function?) The if function in main.c . I single stepped and put a breakpoint on the if func. and under the hood program goes on the "if(looped == 1)" and at that point debugger shows looped being 0. And yet it still enters the if anyway. I didnt put a breakpoint to Rx but all data is received correctly. All 8 caharchters are received in one go and then transmitted in one go. They are like seperate actions. – The Blitz Sep 01 '23 at 11:18
  • Single-step in disasm rather than in the C source. Observe content of relevant registers to find out, why is the if() entered. – wek Sep 01 '23 at 11:28
  • @wek As for the deviation: starts at the second 8 chars. 2nd 8 chars are transmitted to screen, txeie is cleared and it should end there. Yet in the main.c it wrongly goes in the if again, as a result re sets the TXEIE and thus sends the 2nd 8 chars one more time. Then it magically corrects itself. In other words deviation only happens once for each group. Ill try to remove whiles from the interrupt as per Ilya's suggestion – The Blitz Sep 01 '23 at 11:31
  • _thus sends the 2nd 8 chars one more time_ in the code you've posted it shouldn't - as after sending 8 chars the first time looped = 0 at the same time when x.tailpointer = 0, and x.headpointer = 0 in each pass of the while loop, when entering ISR the second time both xheadpointer and x.tailpointer are zero, thus body of while (x.headpointer != x.tailpointer) is never executed. I suspect you did not post exactly the code of which behaviour you are describing - either you've changed the source meantime, or you are debugging a different binary which does not result from that source. – wek Sep 01 '23 at 12:08
  • Which STM32 are we talking about, btw? – wek Sep 01 '23 at 12:13
  • @Ilya, removing whiles worked thank you! Although i must add adding and removing breakpoints during the debugging process seems to mess up the program. – The Blitz Sep 01 '23 at 12:18
  • @wek Good catch! I havent changed the code, only got rid of whiles to make it work. I think whiles were messing up the code. I mean it was entering the "if" in main function when it shouldnt have been, so im not too surprised it entered the transmit section when it shouldnt have. Thank you for your help too :) – The Blitz Sep 01 '23 at 12:38

0 Answers0