Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* USER CODE BEGIN Header */
- /**
- ******************************************************************************
- * @file : main.c
- * @brief : Main program body
- ******************************************************************************
- ** This notice applies to any and all portions of this file
- * that are not between comment pairs USER CODE BEGIN and
- * USER CODE END. Other portions of this file, whether
- * inserted by the user or by software development tools
- * are owned by their respective copyright owners.
- *
- * COPYRIGHT(c) 2020 STMicroelectronics
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of STMicroelectronics nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- ******************************************************************************
- */
- /* USER CODE END Header */
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
- /* Private includes ----------------------------------------------------------*/
- /* USER CODE BEGIN Includes */
- #include "string.h"
- #include "stdarg.h"
- #define FRAME_STARTSTOP 0x30 //znak początku i końca ramki "zero" 0
- #define UNIK_PZU 0x2D //myślnik, Pierwszy Znak Ucieczki "minus" -
- #define UNIK_DZU 0x2E //kropka, Drugi Znak Ucieczki "kropka" .
- #define UNIKANIE_UNIKU 0x2F //aby móc wpisac w ramce myślnik /. to - "slash" /
- #define CONTINUUE 10
- #define UNIK1 20
- #define UNIK2 30
- #define INFORMATION_MESSAGE 40
- #define FRAME_RECEIVED 50
- #define BUFF_SIZE 256 //zdefiniowanie rozmiaru bufora
- #define COMMAND_SIZE 128 //zdefiniowanie rozmiaru komendy
- /* USER CODE END Includes */
- /* Private typedef -----------------------------------------------------------*/
- /* USER CODE BEGIN PTD */
- uint8_t frame_state = CONTINUUE;
- int idx;
- char Buff_Rx[BUFF_SIZE]; //zdefiniowanie rozmiaru bufora odbiorczego (256)
- char Buff_Tx[BUFF_SIZE]; //zdefiniowanie rozmiaru bufora nadawczego (256)
- volatile uint8_t Busy_Tx = 0;//deklaracja wskaźnika na bufory 0 (zmienna ulotna)
- volatile uint8_t Empty_Tx = 0; //deklaracja wskaźnika na bufory 0
- volatile uint8_t Busy_Rx = 0; //deklaracja wskaźnika na bufory 0
- volatile uint8_t Empty_Rx = 0; //deklaracja wskaźnika na bufory 0
- uint8_t pir_state = 0; //zdefiniowanie stanu czujki na 0
- uint8_t previous_pir_state = 0; //zdefiniowanie poprzedniego stanu czujki na 0
- uint8_t alarm_armed = 0; //zdefiniowanie uzbrojenia alarmu na 0
- uint8_t ap, p; //zdefiniowanie zmiennych potrzebnych do alarmu
- uint8_t bajty_idle = 0; //zdefiniowanie bufora ramki na 0
- char frame[128]; //zdefiniowanie rozmiaru ramki
- char command[COMMAND_SIZE]; //zdefiniowanie rozmiaru komendy command=128
- /* 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 ---------------------------------------------------------*/
- TIM_HandleTypeDef htim4;
- UART_HandleTypeDef huart2;
- /* USER CODE BEGIN PV */
- /* USER CODE END PV */
- /* Private function prototypes -----------------------------------------------*/
- void SystemClock_Config(void);
- static void MX_GPIO_Init(void);
- static void MX_USART2_UART_Init(void);
- static void MX_TIM4_Init(void);
- /* USER CODE BEGIN PFP */
- /* USER CODE END PFP */
- /* Private user code ---------------------------------------------------------*/
- /* USER CODE BEGIN 0 */
- void USART_fsend(char* format, ...) {
- char tmp_rs[128]; //zmiena tymczasowa tmp_rs
- int i;
- __IO int idx; // __IO to zmienna typu volatile, indeks naszej tablicy
- va_list valist; //tworzymy liste ze zmienna iloscia argumentu
- va_start(valist, format);//inicjalizuje valist, aby pobrac dodatkowe argumenty po parametrze
- vsprintf(tmp_rs, format, valist); //do zmiennej tmp_rs jest zapisywany sformatowany juz ciag znaków
- va_end(valist); //zakonczenie listy ze zmienna iloscia argumentow
- idx = Empty_Tx; //naszemu indeksowi przypisujemy wskaznik na puste miejsce w buforze nadawczym
- for (i = 0; i < strlen(tmp_rs); i++) { //wypelniamy bufor nadawczy znakami
- Buff_Tx[idx] = tmp_rs[i];
- idx++;
- if (idx >= BUFF_SIZE)//jezeli przekroczymy rozmiar bufora, to zerujemy index
- idx = 0;
- }
- __disable_irq(); //wylaczamy przerwania
- if ((Empty_Tx == Busy_Tx)
- && (__HAL_UART_GET_FLAG(&huart2,UART_FLAG_TXE) == SET))
- //jezeli wskazniki na puste i zajete miejsce w buforze sa takie same oraz UART nic nie nadaje oraz UART nic nie nadaje
- { //to rozpoczynamy wysylanie
- Empty_Tx = idx; //przypisujemy wskazniki pustego miejsca nasz indeks
- uint8_t tmp = Buff_Tx[Busy_Tx]; //zmiennej tymczasowej przypisujemy element z bufora nadawczego
- Busy_Tx++; //zwiekszamy wskaznik miejsca zajetego w buforze
- if (Busy_Tx >= BUFF_SIZE)//jezeli przekroczymy zakres bufora to... zerujemy wskaznik
- Busy_Tx = 0;
- HAL_UART_Transmit_IT(&huart2, &tmp, 1); //transmitujemy pierwszy bajt danych
- //po przeslaniu tego bajtu zostanie wywolaane przerwanie z callbacku
- } else {
- Empty_Tx = idx; //wskaznikowi pustego miejsca przypisujemy nasz indeks
- }
- __enable_irq(); //wlaczamy przerwania
- }
- char getchar_BuffRx() //pobierz znak z bufora
- {
- char tmp;
- if (Busy_Rx != Empty_Rx)//jeżeli miejsce zajęte bufora odczytu jest różne od pustego miejsca buforu odczytu
- {
- tmp = Buff_Rx[Busy_Rx]; //pod tymczasową zmiennę, do bufora odczytu wpisz zajęte miejsce bufora odczytu
- Busy_Rx++; //zwiększ wskaźnik
- if (Busy_Rx > BUFF_SIZE)
- Busy_Rx = 0;//jeżeli zajęte miejsce jest większe niż rozmiar bufora, to przejdź do elementu "0" w tym buforze (zatocz koło)
- return tmp;
- }
- return -1;
- }
- void DoCommand(char *comm) //funkcja, która wykonuje polecenie z ramki
- {
- if (strncmp(comm, "alarm", 5) == 0) {
- ap = sscanf(comm, "alarm:%d", &p);//wyszukaj po usarcie kombinacji alarm: tj. 0alarm:costam0
- if (ap == 1) //jeżeli wpisano kombinację alarm:
- {
- alarm_armed = p; //uzbrajamy alarm
- USART_fsend("OK, działamy! \n\r");//wysyłamy komunikat, że alarm został uzbrojony
- } else {
- USART_fsend("Brak wszystkich potrzebnych parametrów! :-( \n\r"); //alarm nie został uzbrojony
- }
- }
- }
- void clear_data() {
- for (int i = 0; i < COMMAND_SIZE; i++)
- command[i] = 0;
- idx = 0;
- }
- void framee() {
- if (Busy_Rx != Empty_Rx) {//jeżeli miejsce zajęte odczytu jest różne od pustego
- char a = getchar_BuffRx(); //ZNAK Z BUFORA
- __HAL_TIM_SET_COUNTER(&htim4, 0); //nasłuch na linii dzięki Timer 4.
- switch (frame_state) //ODCZYT DANYCH
- {
- case CONTINUUE: {
- if (a == FRAME_STARTSTOP) { //a jest znakiem początka ramki - "zero" 0
- clear_data(); //wyczyśc dane
- frame_state = INFORMATION_MESSAGE;//stan ramki na tryb przesył wiadomości (opcja jednego z case)
- }
- break;
- }
- case INFORMATION_MESSAGE: {
- if (a == FRAME_STARTSTOP) { //znak przesłany z bufora odbiorczego to znak początku i końca ramki
- DoCommand(command); //command wykonuje polecenie z ramki
- clear_data(); //wyczysc dane
- frame_state = INFORMATION_MESSAGE;///stan ramki na tryb przesył wiadomości (opcja jednego z case)
- } else if (a == UNIK_PZU)//jeżeli znak to pierwszy znak uniku to...
- frame_state = UNIK2;//status ramki będzie interpretowany w case UNIK2 który jest niżej żeby sprawdzic czy bedzie drugi znak uniku
- //zwiększenie wskaźnika //zawiesi sie
- }
- break;
- }
- case UNIK2:
- {
- if (a == UNIK_DZU) { //jeżeli odebrany znak to drugi znak uniku
- command[idx] = FRAME_STARTSTOP; //to nasza ramka się nie wysypie i mmożemy korzystac w środku treści ramki z "zera"
- ///////
- if (idx >= 30) {
- frame_state = CONTINUUE;
- USART_fsend = "nieprawidłowe dane";
- break;
- } else {
- command[idx] = a;
- if (idx >= 30) {
- frame_state = CONTINUUE;
- USART_fsend = "nieprawidłowe dane";
- break;
- } else {
- idx++;
- }
- }
- break;
- } else if (a == UNIKANIE_UNIKU) { //aby moc w ramce wpisac "myślnik" -
- command[idx] = UNIK_PZU;//slash z myślnikiem jest po to, aby moc wpisac do ramki myślnik
- if (idx >= 30) {
- frame_state = CONTINUUE;
- USART_fsend = "nieprawidłowe dane";
- break;
- } else {
- idx++;
- }
- } else {
- frame_state = CONTINUUE;
- USART_fsend("nieprawidłowe dane");
- break;
- }
- frame_state = INFORMATION_MESSAGE; //znak jest znakiem ramki
- break;
- }
- }
- }
- }
- uint8_t pir_move() { //ustawienie PINu dla czujki ruchu
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)
- return 1;
- else
- return 0;
- }
- uint8_t pir_move_detected() {
- previous_pir_state = pir_state; //na początku oba są zerami (zadeklarowane w #include)
- pir_state = pir_move(); //stan ruchu pobierz z funkcji pirmove
- if (previous_pir_state == 0 && pir_state == 1) //jeżeli wykryto ruch (stan wysoki w obecnym pir_state)
- return 1;
- else
- return 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();
- MX_TIM4_Init();
- /* USER CODE BEGIN 2 */
- HAL_UART_Receive_IT(&huart2, (uint8_t *) &Buff_Rx[Empty_Rx], 1);
- USART_fsend("Halo to ja, Twój STM32! :-)\n\r"); //komunikat powitalny po USARTcie
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1) {
- /* USER CODE END WHILE */
- framee(); //funkcja z ramką
- if (pir_move_detected()) //jeżeli wykryto ruch (który sprawdza funkcja pir_move_detected)
- {
- if (alarm_armed) //jeżeli alarm jest uzbrojony
- {
- USART_fsend("I see you, sioooooooo :-) \n\r"); //wypisz po USART
- }
- }
- /* USER CODE BEGIN 3 */
- }
- /* USER CODE END 3 */
- }
- /**
- * @brief System Clock Configuration
- * @retval None
- */
- void SystemClock_Config(void) {
- RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
- RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
- RCC_PeriphCLKInitTypeDef PeriphClkInit = { 0 };
- /**Initializes the CPU, AHB and APB busses clocks
- */
- 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 = 1;
- RCC_OscInitStruct.PLL.PLLN = 10;
- RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
- 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 busses 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();
- }
- PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
- PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
- if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
- Error_Handler();
- }
- /**Configure the main internal regulator output voltage
- */
- if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) {
- Error_Handler();
- }
- }
- /**
- * @brief TIM4 Initialization Function
- * @param None
- * @retval None
- */
- static void MX_TIM4_Init(void) {
- /* USER CODE BEGIN TIM4_Init 0 */
- /* USER CODE END TIM4_Init 0 */
- TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
- TIM_MasterConfigTypeDef sMasterConfig = { 0 };
- /* USER CODE BEGIN TIM4_Init 1 */
- /* USER CODE END TIM4_Init 1 */
- htim4.Instance = TIM4;
- htim4.Init.Prescaler = 31;
- htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
- htim4.Init.Period = 103;
- htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
- if (HAL_TIM_Base_Init(&htim4) != HAL_OK) {
- Error_Handler();
- }
- sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
- if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK) {
- Error_Handler();
- }
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
- if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK) {
- Error_Handler();
- }
- /* USER CODE BEGIN TIM4_Init 2 */
- /* USER CODE END TIM4_Init 2 */
- }
- /**
- * @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 = 9600;
- 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.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- if (HAL_UART_Init(&huart2) != HAL_OK) {
- Error_Handler();
- }
- /* USER CODE BEGIN USART2_Init 2 */
- /* 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 };
- /* GPIO Ports Clock Enable */
- __HAL_RCC_GPIOC_CLK_ENABLE()
- ;
- __HAL_RCC_GPIOH_CLK_ENABLE()
- ;
- __HAL_RCC_GPIOA_CLK_ENABLE()
- ;
- __HAL_RCC_GPIOB_CLK_ENABLE()
- ;
- /*Configure GPIO pin : B1_Pin */
- GPIO_InitStruct.Pin = B1_Pin;
- GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
- /*Configure GPIO pin : PA5 */
- GPIO_InitStruct.Pin = GPIO_PIN_5;
- GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- }
- /* USER CODE BEGIN 4 */
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
- if (huart == &huart2) //sprawdzenie, czy funkcja była wywołana przez interfejs USART2
- {
- Empty_Rx++; //zwiększenie wskaźnika na miejsce puste w buforze
- if (Empty_Rx >= BUFF_SIZE) //jeżeli wskaźnik przekroczył rozmiar bufora
- Empty_Rx = 0; //wróc do początku (zatocz koło)
- HAL_UART_Receive_IT(&huart2, (uint8_t *) &Buff_Rx[Empty_Rx], 1);
- } //oczekuj na odbiór jednego kolejnego znaku na przerwaniu z interfejsu USART2
- } //do pustego miejsca w buforze odbiorczym
- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
- if (huart == &huart2) //sprawdzenie, czy funkcja była wywołana przez interfejs USART2
- {
- if (Empty_Tx != Busy_Tx) {//jeżeli wskaźnik miejsca zajętego i miejsca pustego w buforze nadawczym się różnią (bufor nie jest pusty)
- uint8_t tmp = Buff_Tx[Busy_Tx]; //przypisanie do tymczasowej zmiennej tmp znaku znajdującego się w ostatnim zajętym miejscuw buforze nadawczym
- Busy_Tx++; //zwiększenie wskaźnika na zajęte miejsce w buforze nadawczym
- if (Busy_Tx >= BUFF_SIZE) //jeżeli wskaźnik przekroczył rozmiar bufora, wróc do początku (zatocz koło)
- Busy_Tx = 0;
- HAL_UART_Transmit_IT(&huart2, &tmp, 1); //prześlij na przerwaniu jeden znak znajdujący się w zmiennej tmp (traktuj tą zmienną jak jednoelementowy bufor)
- }
- }
- }
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
- if (htim == &htim4) {
- if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)
- == SET&& frame_state == INFORMATION_MESSAGE) //
- {
- bajty_idle++;
- if (bajty_idle >= 3) //jeśli nic nie jest nadawane na linii przez 3 sekundy, to czyścimy bufor ramki do czasu wystąpienia ciszy (nie bufor odbiorczy)
- { //o ile nie nie nastąpił znak końca ramki.
- bajty_idle = 0;
- frame_state = CONTINUUE;
- }
- }
- }
- }
- /* 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 */
- /* 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(char *file, uint32_t line)
- {
- /* USER CODE BEGIN 6 */
- /* User can add his own implementation to report the file name and line number,
- tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
- /* USER CODE END 6 */
- }
- #endif /* USE_FULL_ASSERT */
- /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement