diff --git a/.gitignore b/.gitignore index 5e67a35..324b2e1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ .metadata Core/Src/Backup/ Core/Inc/Backup/ -Debug +.bak +.gitignore +can_logger Debug.launch diff --git a/Core/Inc/canlog.h b/Core/Inc/canlog.h index 0636e2c..d741aff 100644 --- a/Core/Inc/canlog.h +++ b/Core/Inc/canlog.h @@ -1,7 +1,53 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : canlog.h + * @brief : Header for canlog.c file. + * This file contains the defines related to SAE J1939 CAN. + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Erick Ahmed. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ #ifndef CANLOG_H #define CANLOG_H #include "main.h" +#include "cmsis_os.h" + +extern osMessageQueueId_t xCAN1RxQueue; +extern osMessageQueueId_t xCAN2RxQueue; +extern osSemaphoreId_t xSemaphoreCAN1; +extern osSemaphoreId_t xSemaphoreCAN2; + +/* USER CODE BEGIN PTD */ +typedef struct { + GPIO_TypeDef* port; + uint16_t pin; +} LED_Config; + +typedef struct { + uint32_t id; + uint8_t payload[8]; + uint8_t dlc; + uint8_t isExtended; + //uint32_t timestamp; //Enable TIM2: 42 MHz / (41+1) = 1 MHz → 1 µs tick; 32bit autoreload freerunning + // this is for getting timestamps on can messages +} CanMessage_t; + +typedef struct { + LED_Config *led; + osSemaphoreId_t semaphore; + osTimerId_t timer; +} LEDContext; +/* USER CODE END PTD */ /** * @brief Initializes the CAN logger modules, OS threads, queues, and hardware. @@ -9,5 +55,8 @@ * @param hcan2 Pointer to the CAN2 handle */ void CAN_Logger_Init(CAN_HandleTypeDef *hcan1, CAN_HandleTypeDef *hcan2); +void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan); +void vCANLoggerListen(void *argument); +void vLEDHeartbeat(void *argument); #endif /* CANLOG_H */ diff --git a/Core/Inc/main.h b/Core/Inc/main.h index b31c169..a2e2207 100644 --- a/Core/Inc/main.h +++ b/Core/Inc/main.h @@ -8,11 +8,9 @@ * @attention * * Copyright (c) 2026 STMicroelectronics. - * All rights reserved. + * Copyright (c) 2026 Erick Ahmed. * - * 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. + * SPDX-License-Identifier: GPL-3.0-or-later * ****************************************************************************** */ @@ -28,6 +26,7 @@ extern "C" { /* Includes ------------------------------------------------------------------*/ #include "stm32f4xx_hal.h" +#include "canlog.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ diff --git a/Core/Src/canlog.c b/Core/Src/canlog.c index 8efc726..45b2935 100644 --- a/Core/Src/canlog.c +++ b/Core/Src/canlog.c @@ -1,40 +1,143 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : canlog.c + * @brief : SAE J1939 CAN Real Time listening and decoding + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 Erick Ahmed. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ #include "canlog.h" +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ #include "cmsis_os.h" #include +/* USER CODE END Includes */ -/* Definitions for canBus1Listen */ -osThreadId_t canBus1ListenHandle; -const osThreadAttr_t canBus1Listen_attributes = { - .name = "canBus1Listen", - .stack_size = 128 * 4, - .priority = (osPriority_t) osPriorityRealtime1, -}; -/* Definitions for canBus2Listen */ -osThreadId_t canBus2ListenHandle; -const osThreadAttr_t canBus2Listen_attributes = { - .name = "canBus2Listen", - .stack_size = 128 * 4, - .priority = (osPriority_t) osPriorityRealtime, -}; +extern CAN_HandleTypeDef hcan1; +extern CAN_HandleTypeDef hcan2; /* Private function prototypes -----------------------------------------------*/ -void CAN_Logger_Init(CAN_HandleTypeDef *hcan1, CAN_HandleTypeDef *hcan2){} -static void CAN_Listen(void *argument){} - -/* BEGIN Header_CAN_Listen */ +/* USER CODE BEGIN FunctionPrototypes */ +/* BEGIN CAN_Logger_Init */ /** - * @brief Function implementing the canBus0Listen thread. + * @brief Initialize CAN bus logger. + * @param argument: hcan1, hcan2 + * @retval None + */ +void CAN_Logger_Init(CAN_HandleTypeDef *hcan1, CAN_HandleTypeDef *hcan2) +{ + CAN_FilterTypeDef filter = {0}; + filter.FilterMode = CAN_FILTERMODE_IDMASK; + filter.FilterScale = CAN_FILTERSCALE_32BIT; + filter.FilterIdHigh = 0x0000; + filter.FilterMaskIdHigh = 0x0000; + filter.FilterIdLow = 0x0000; + filter.FilterMaskIdLow = 0x0000; + filter.FilterFIFOAssignment = CAN_FILTER_FIFO0; + filter.FilterActivation = ENABLE; + filter.SlaveStartFilterBank = 14; + + filter.FilterBank = 0; + if (HAL_CAN_ConfigFilter(hcan1, &filter) != HAL_OK) Error_Handler(); + filter.FilterBank = 14; + if (HAL_CAN_ConfigFilter(hcan2, &filter) != HAL_OK) Error_Handler(); + + if (HAL_CAN_Start(hcan1) != HAL_OK) Error_Handler(); + if (HAL_CAN_Start(hcan2) != HAL_OK) Error_Handler(); + + if (HAL_CAN_ActivateNotification(hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) Error_Handler(); + if (HAL_CAN_ActivateNotification(hcan2, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) Error_Handler(); +} +/* END CAN_Logger_Init */ + +/* BEGIN HAL_CAN_RxFifo0MsgPendingCallback */ +/** + * @brief ISR for CAN message pending in FIFO0 + * @param argument: Can handle hcan (hcan1 or hcan2) + * @retval None + */ +void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) +{ + /* CODE BEGIN */ + CanMessage_t message; + CAN_RxHeaderTypeDef rxHeader; + uint8_t data[8]; + + if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rxHeader, data) != HAL_OK) Error_Handler(); + + message.id = rxHeader.ExtId; + message.dlc = rxHeader.DLC; + message.isExtended = (rxHeader.IDE == CAN_ID_EXT); + memcpy(message.payload, data, rxHeader.DLC); + + osMessageQueueId_t queue = (hcan->Instance == CAN1) ? xCAN1RxQueue : xCAN2RxQueue; + if (osMessageQueuePut(queue, &message, 0U, 0U) != osOK) + { + // TODO: better handling: flash error_led on osTimeout, call Error_Handler() when other errors + + // send errors like queue full via UART + } + /* CODE END */ +} +/* END HAL_CAN_RxFifo0MsgPendingCallback */ + +/* BEGIN vCANLoggerListen */ +/** + * @brief Log incoming CAN bus traffic in FIFO buffer * @param argument: Not used * @retval None */ -/* END Header_CAN_Listen */ -void CAN_Listen(void *argument) +void vCANLoggerListen(void *argument) +{ + /* CODE BEGIN */ + CAN_HandleTypeDef *hcan = (CAN_HandleTypeDef*)argument; + osMessageQueueId_t queue; + CanMessage_t message; + + queue = (hcan->Instance == CAN1) ? xCAN1RxQueue : xCAN2RxQueue; + + for (;;) + { + if (osMessageQueueGet(queue, &message, NULL, osWaitForever) == osOK) + { + osSemaphoreRelease( (hcan->Instance == CAN1) ? xSemaphoreCAN1 : xSemaphoreCAN2 ); + // TODO: use this semaphore + } + + } + /* CODE END */ +} +/* END vCANLoggerListen */ + +/* BEGIN vLEDHeartbeat */ +/** + * @brief Blink a LED in heartbeat mode when CAN traffic is detected + * @param argument: LED GPIO (port and pin) + * @retval None + */ +void vLEDHeartbeat(void *argument) { /* CODE BEGIN */ - /* Infinite loop */ - for(;;) + LEDContext *context = (LEDContext*)argument; + + if (osSemaphoreAcquire(context->semaphore, 0U) == osOK) { - osDelay(1); + HAL_GPIO_WritePin(context->led->port, context->led->pin, GPIO_PIN_SET); + osTimerStart(context->timer, 25U); + } + else + { + HAL_GPIO_WritePin(context->led->port, context->led->pin, GPIO_PIN_RESET); } /* CODE END */ } +/* END vLEDHeartbeat */ +/* USER CODE END FunctionPrototypes */ diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c deleted file mode 100644 index 95f65a7..0000000 --- a/Core/Src/freertos.c +++ /dev/null @@ -1,59 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : freertos.c - * Description : Code for freertos applications - ****************************************************************************** - * @attention - * - * Copyright (c) 2026 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 "FreeRTOS.h" -#include "task.h" -#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 ---------------------------------------------------------*/ -/* USER CODE BEGIN Variables */ - -/* USER CODE END Variables */ - -/* Private function prototypes -----------------------------------------------*/ -/* USER CODE BEGIN FunctionPrototypes */ - -/* USER CODE END FunctionPrototypes */ - -/* Private application code --------------------------------------------------*/ -/* USER CODE BEGIN Application */ - -/* USER CODE END Application */ - diff --git a/Core/Src/main.c b/Core/Src/main.c index 51368a2..ff7bb39 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -1,460 +1,526 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : main.c - * @brief : Main program body - ****************************************************************************** - * @attention - * - * Copyright (c) 2026 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 "canlog.h" -#include "cmsis_os.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 ---------------------------------------------------------*/ -CAN_HandleTypeDef hcan1; -CAN_HandleTypeDef hcan2; - -SD_HandleTypeDef hsd; -DMA_HandleTypeDef hdma_sdio_rx; -DMA_HandleTypeDef hdma_sdio_tx; - -/* USER CODE BEGIN PV */ - -/* USER CODE END PV */ - -/* Private function prototypes -----------------------------------------------*/ -void SystemClock_Config(void); -static void MX_GPIO_Init(void); -static void MX_DMA_Init(void); -static void MX_CAN1_Init(void); -static void MX_CAN2_Init(void); -static void MX_SDIO_SD_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_DMA_Init(); - MX_CAN1_Init(); - MX_CAN2_Init(); - MX_SDIO_SD_Init(); - /* USER CODE BEGIN 2 */ - - // Initialize CAN1 and CAN2 - CAN_Logger_Init(&hcan1, &hcan2); - - /* USER CODE END 2 */ - - /* Init scheduler */ - osKernelInitialize(); - - /* USER CODE BEGIN RTOS_MUTEX */ - /* add mutexes, ... */ - /* USER CODE END RTOS_MUTEX */ - - /* USER CODE BEGIN RTOS_SEMAPHORES */ - /* add semaphores, ... */ - /* USER CODE END RTOS_SEMAPHORES */ - - /* USER CODE BEGIN RTOS_TIMERS */ - /* start timers, add new ones, ... */ - /* USER CODE END RTOS_TIMERS */ - - /* USER CODE BEGIN RTOS_QUEUES */ - /* add queues, ... */ - /* USER CODE END RTOS_QUEUES */ - - /* USER CODE BEGIN RTOS_THREADS */ - /* add threads, ... */ - /* USER CODE END RTOS_THREADS */ - - /* USER CODE BEGIN RTOS_EVENTS */ - /* add events, ... */ - /* USER CODE END RTOS_EVENTS */ - - /* Start scheduler */ - osKernelStart(); - - /* We should never get here as control is now taken by the scheduler */ - - /* Infinite loop */ - /* USER CODE BEGIN WHILE */ - while (1) - { - /* USER CODE END WHILE */ - - /* 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}; - - /** Configure the main internal regulator output voltage - */ - __HAL_RCC_PWR_CLK_ENABLE(); - __HAL_PWR_VOLTAGESCALING_CONFIG(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_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 = 16; - RCC_OscInitStruct.PLL.PLLN = 336; - RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; - RCC_OscInitStruct.PLL.PLLQ = 7; - 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_DIV4; - RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV4; - - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) - { - Error_Handler(); - } -} - -/** - * @brief CAN1 Initialization Function - * @param None - * @retval None - */ -static void MX_CAN1_Init(void) -{ - - /* USER CODE BEGIN CAN1_Init 0 */ - - /* USER CODE END CAN1_Init 0 */ - - /* USER CODE BEGIN CAN1_Init 1 */ - - /* USER CODE END CAN1_Init 1 */ - hcan1.Instance = CAN1; - hcan1.Init.Prescaler = 4; - hcan1.Init.Mode = CAN_MODE_SILENT; - hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; - hcan1.Init.TimeSeg1 = CAN_BS1_16TQ; - hcan1.Init.TimeSeg2 = CAN_BS2_4TQ; - hcan1.Init.TimeTriggeredMode = DISABLE; - hcan1.Init.AutoBusOff = DISABLE; - hcan1.Init.AutoWakeUp = DISABLE; - hcan1.Init.AutoRetransmission = DISABLE; - hcan1.Init.ReceiveFifoLocked = DISABLE; - hcan1.Init.TransmitFifoPriority = DISABLE; - if (HAL_CAN_Init(&hcan1) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN CAN1_Init 2 */ - - /* USER CODE END CAN1_Init 2 */ - -} - -/** - * @brief CAN2 Initialization Function - * @param None - * @retval None - */ -static void MX_CAN2_Init(void) -{ - - /* USER CODE BEGIN CAN2_Init 0 */ - - /* USER CODE END CAN2_Init 0 */ - - /* USER CODE BEGIN CAN2_Init 1 */ - - /* USER CODE END CAN2_Init 1 */ - hcan2.Instance = CAN2; - hcan2.Init.Prescaler = 8; - hcan2.Init.Mode = CAN_MODE_SILENT; - hcan2.Init.SyncJumpWidth = CAN_SJW_1TQ; - hcan2.Init.TimeSeg1 = CAN_BS1_16TQ; - hcan2.Init.TimeSeg2 = CAN_BS2_4TQ; - hcan2.Init.TimeTriggeredMode = DISABLE; - hcan2.Init.AutoBusOff = DISABLE; - hcan2.Init.AutoWakeUp = DISABLE; - hcan2.Init.AutoRetransmission = DISABLE; - hcan2.Init.ReceiveFifoLocked = DISABLE; - hcan2.Init.TransmitFifoPriority = DISABLE; - if (HAL_CAN_Init(&hcan2) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN CAN2_Init 2 */ - - /* USER CODE END CAN2_Init 2 */ - -} - -/** - * @brief SDIO Initialization Function - * @param None - * @retval None - */ -static void MX_SDIO_SD_Init(void) -{ - - /* USER CODE BEGIN SDIO_Init 0 */ - - /* USER CODE END SDIO_Init 0 */ - - /* USER CODE BEGIN SDIO_Init 1 */ - - /* USER CODE END SDIO_Init 1 */ - hsd.Instance = SDIO; - hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; - hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; - hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE; - hsd.Init.BusWide = SDIO_BUS_WIDE_1B; - hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; - hsd.Init.ClockDiv = 0; - if (HAL_SD_Init(&hsd) != HAL_OK) - { - Error_Handler(); - } - if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN SDIO_Init 2 */ - - /* USER CODE END SDIO_Init 2 */ - -} - -/** - * Enable DMA controller clock - */ -static void MX_DMA_Init(void) -{ - - /* DMA controller clock enable */ - __HAL_RCC_DMA2_CLK_ENABLE(); - - /* DMA interrupt init */ - /* DMA2_Stream3_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); - /* DMA2_Stream6_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); - -} - -/** - * @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_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOH_CLK_ENABLE(); - __HAL_RCC_GPIOA_CLK_ENABLE(); - __HAL_RCC_GPIOB_CLK_ENABLE(); - __HAL_RCC_GPIOD_CLK_ENABLE(); - - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5, GPIO_PIN_RESET); - - /*Configure GPIO pins : PC13 PC14 PC15 PC0 - PC1 PC2 PC3 PC4 - PC5 PC6 PC7 */ - GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0 - |GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4 - |GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - /*Configure GPIO pins : PH0 PH1 */ - GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); - - /*Configure GPIO pins : PA0 PA1 PA2 PA3 - PA4 PA5 PA6 PA7 - PA8 PA9 PA10 PA11 - PA12 PA13 PA14 PA15 */ - GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 - |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7 - |GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 - |GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - /*Configure GPIO pins : PB0 PB1 PB2 PB10 - PB11 PB14 PB15 PB6 - PB7 */ - GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10 - |GPIO_PIN_11|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_6 - |GPIO_PIN_7; - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); - - /*Configure GPIO pins : PB3 PB4 PB5 */ - GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(GPIOB, &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 Period elapsed callback in non blocking mode - * @note This function is called when TIM6 interrupt took place, inside - * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment - * a global variable "uwTick" used as application time base. - * @param htim : TIM handle - * @retval None - */ -void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) -{ - /* USER CODE BEGIN Callback 0 */ - - /* USER CODE END Callback 0 */ - if (htim->Instance == TIM6) - { - HAL_IncTick(); - } - /* USER CODE BEGIN Callback 1 */ - - /* USER CODE END Callback 1 */ -} - -/** - * @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 : main.c + * @brief : Main program body + ****************************************************************************** + * @attention + * Copyright (c) 2026 STMicroelectronics. + * Copyright (c) 2026 Erick Ahmed. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ +#include "main.h" +#include "cmsis_os.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +#include "canlog.h" +/* 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 ---------------------------------------------------------*/ +CAN_HandleTypeDef hcan1; +CAN_HandleTypeDef hcan2; + +SD_HandleTypeDef hsd; +DMA_HandleTypeDef hdma_sdio_rx; +DMA_HandleTypeDef hdma_sdio_tx; + +/* USER CODE BEGIN PV */ +LED_Config led_can1 = {GPIOB, GPIO_PIN_2}; +LED_Config led_can2 = {GPIOB, GPIO_PIN_5}; +LED_Config led_error = {GPIOB, GPIO_PIN_3}; + +LEDContext ledContextCAN1; +LEDContext ledContextCAN2; + +osSemaphoreId_t xSemaphoreCAN1; +osSemaphoreId_t xSemaphoreCAN2; + +osTimerId_t xHeartbeatTimerCAN1; +osTimerId_t xHeartbeatTimerCAN2; + +osMessageQueueId_t xCAN1RxQueue; +osMessageQueueId_t xCAN2RxQueue; +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +void SystemClock_Config(void); +static void MX_GPIO_Init(void); +static void MX_DMA_Init(void); +static void MX_CAN1_Init(void); +static void MX_CAN2_Init(void); +static void MX_SDIO_SD_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_DMA_Init(); + MX_CAN1_Init(); + MX_CAN2_Init(); + MX_SDIO_SD_Init(); + + /* USER CODE BEGIN 2 */ + CAN_Logger_Init(&hcan1, &hcan2); + /* USER CODE END 2 */ + + /* Init scheduler */ + osKernelInitialize(); + + /* USER CODE BEGIN RTOS_TASKS */ + osThreadId_t xCAN1rx; + const osThreadAttr_t CAN1rxAttributes = { + .name = "CAN1rx", + .stack_size = 128 * 4, + .priority = (osPriority_t) osPriorityRealtime1, + }; + osThreadId_t xCAN2rx; + const osThreadAttr_t CAN2rxAttributes = { + .name = "CAN2rx", + .stack_size = 128 * 4, + .priority = (osPriority_t) osPriorityRealtime, + }; + /* USER END RTOS_TASKS */ + + /* USER CODE BEGIN RTOS_MUTEX */ + /* add mutexes, ... */ + /* USER CODE END RTOS_MUTEX */ + + /* USER CODE BEGIN RTOS_SEMAPHORES */ + xSemaphoreCAN1 = osSemaphoreNew(10, 0, NULL); + xSemaphoreCAN2 = osSemaphoreNew(10, 0, NULL); + + if (xSemaphoreCAN1 == NULL || xSemaphoreCAN2 == NULL) Error_Handler(); + + ledContextCAN1.led = &led_can1; + ledContextCAN1.semaphore = xSemaphoreCAN1; + ledContextCAN2.led = &led_can2; + ledContextCAN2.semaphore = xSemaphoreCAN2; + /* USER CODE END RTOS_SEMAPHORES */ + + /* USER CODE BEGIN RTOS_TIMERS */ + xHeartbeatTimerCAN1 = osTimerNew(vLEDHeartbeat, osTimerOnce, &ledContextCAN1, NULL); + xHeartbeatTimerCAN2 = osTimerNew(vLEDHeartbeat, osTimerOnce, &ledContextCAN2, NULL); + + osTimerStart(xHeartbeatTimerCAN1, 25U); + osTimerStart(xHeartbeatTimerCAN2, 25U); + + if (xHeartbeatTimerCAN1 == NULL || xHeartbeatTimerCAN2 == NULL) Error_Handler(); + + ledContextCAN1.timer = xHeartbeatTimerCAN1; + ledContextCAN2.timer = xHeartbeatTimerCAN2; + /* USER CODE END RTOS_TIMERS */ + + /* USER CODE BEGIN RTOS_QUEUES */ + xCAN1RxQueue = osMessageQueueNew(32, sizeof(CanMessage_t), NULL); + xCAN2RxQueue = osMessageQueueNew(32, sizeof(CanMessage_t), NULL); + + if (xCAN1RxQueue == NULL || xCAN2RxQueue == NULL) Error_Handler(); + /* USER CODE END RTOS_QUEUES */ + + /* USER CODE BEGIN RTOS_THREADS */ + xCAN1rx = osThreadNew(vCANLoggerListen, &hcan1, &CAN1rxAttributes); + xCAN2rx = osThreadNew(vCANLoggerListen, &hcan2, &CAN2rxAttributes); + /* USER CODE END RTOS_THREADS */ + + /* USER CODE BEGIN RTOS_EVENTS */ + /* add events, ... */ + /* USER CODE END RTOS_EVENTS */ + + /* USER CODE BEGIN 3 */ + ledContextCAN1.led = &led_can1; + ledContextCAN1.semaphore = xSemaphoreCAN1; + ledContextCAN1.timer = xHeartbeatTimerCAN1; + + ledContextCAN2.led = &led_can2; + ledContextCAN2.semaphore = xSemaphoreCAN2; + ledContextCAN2.timer = xHeartbeatTimerCAN2; + /* USER CODE END 3 */ + + /* Start scheduler */ + osKernelStart(); + + /* We should never get here as control is now taken by the scheduler */ + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) + { + /* USER CODE END WHILE */ + + /* USER CODE BEGIN 4 */ + } + /* USER CODE END 4 */ +} + +/** + * @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_RCC_PWR_CLK_ENABLE(); + __HAL_PWR_VOLTAGESCALING_CONFIG(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_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 = 16; + RCC_OscInitStruct.PLL.PLLN = 336; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; + RCC_OscInitStruct.PLL.PLLQ = 7; + 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_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV4; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) + { + Error_Handler(); + } +} + +/** + * @brief CAN1 Initialization Function + * @param None + * @retval None + */ +static void MX_CAN1_Init(void) +{ + + /* USER CODE BEGIN CAN1_Init 0 */ + + /* USER CODE END CAN1_Init 0 */ + + /* USER CODE BEGIN CAN1_Init 1 */ + + /* USER CODE END CAN1_Init 1 */ + hcan1.Instance = CAN1; + hcan1.Init.Prescaler = 4; + hcan1.Init.Mode = CAN_MODE_SILENT; + hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; + hcan1.Init.TimeSeg1 = CAN_BS1_16TQ; + hcan1.Init.TimeSeg2 = CAN_BS2_4TQ; + hcan1.Init.TimeTriggeredMode = DISABLE; + hcan1.Init.AutoBusOff = DISABLE; + hcan1.Init.AutoWakeUp = ENABLE; + hcan1.Init.AutoRetransmission = DISABLE; + hcan1.Init.ReceiveFifoLocked = DISABLE; + hcan1.Init.TransmitFifoPriority = DISABLE; + if (HAL_CAN_Init(&hcan1) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN CAN1_Init 2 */ + + /* USER CODE END CAN1_Init 2 */ + +} + +/** + * @brief CAN2 Initialization Function + * @param None + * @retval None + */ +static void MX_CAN2_Init(void) +{ + + /* USER CODE BEGIN CAN2_Init 0 */ + + /* USER CODE END CAN2_Init 0 */ + + /* USER CODE BEGIN CAN2_Init 1 */ + + /* USER CODE END CAN2_Init 1 */ + hcan2.Instance = CAN2; + hcan2.Init.Prescaler = 8; + hcan2.Init.Mode = CAN_MODE_SILENT; + hcan2.Init.SyncJumpWidth = CAN_SJW_1TQ; + hcan2.Init.TimeSeg1 = CAN_BS1_16TQ; + hcan2.Init.TimeSeg2 = CAN_BS2_4TQ; + hcan2.Init.TimeTriggeredMode = DISABLE; + hcan2.Init.AutoBusOff = DISABLE; + hcan2.Init.AutoWakeUp = ENABLE; + hcan2.Init.AutoRetransmission = DISABLE; + hcan2.Init.ReceiveFifoLocked = DISABLE; + hcan2.Init.TransmitFifoPriority = DISABLE; + if (HAL_CAN_Init(&hcan2) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN CAN2_Init 2 */ + + /* USER CODE END CAN2_Init 2 */ + +} + +/** + * @brief SDIO Initialization Function + * @param None + * @retval None + */ +static void MX_SDIO_SD_Init(void) +{ + + /* USER CODE BEGIN SDIO_Init 0 */ + + /* USER CODE END SDIO_Init 0 */ + + /* USER CODE BEGIN SDIO_Init 1 */ + + /* USER CODE END SDIO_Init 1 */ + hsd.Instance = SDIO; + hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; + hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; + hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE; + hsd.Init.BusWide = SDIO_BUS_WIDE_1B; + hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; + hsd.Init.ClockDiv = 0; + if (HAL_SD_Init(&hsd) != HAL_OK) + { + Error_Handler(); + } + if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN SDIO_Init 2 */ + + /* USER CODE END SDIO_Init 2 */ + +} + +/** + * Enable DMA controller clock + */ +static void MX_DMA_Init(void) +{ + + /* DMA controller clock enable */ + __HAL_RCC_DMA2_CLK_ENABLE(); + + /* DMA interrupt init */ + /* DMA2_Stream3_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); + /* DMA2_Stream6_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); + +} + +/** + * @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_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOH_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + + /*Configure GPIO pin Output Level */ + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5, GPIO_PIN_RESET); + + /*Configure GPIO pins : PC13 PC14 PC15 PC0 + PC1 PC2 PC3 PC4 + PC5 PC6 PC7 */ + GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0 + |GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4 + |GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + /*Configure GPIO pins : PH0 PH1 */ + GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); + + /*Configure GPIO pins : PA0 PA1 PA2 PA3 + PA4 PA5 PA6 PA7 + PA8 PA9 PA10 PA11 + PA12 PA13 PA14 PA15 */ + GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 + |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7 + |GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 + |GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /*Configure GPIO pins : PB0 PB1 PB2 PB10 + PB11 PB14 PB15 PB6 + PB7 */ + GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10 + |GPIO_PIN_11|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_6 + |GPIO_PIN_7; + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /*Configure GPIO pins : PB3 PB4 PB5 */ + GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /* USER CODE BEGIN MX_GPIO_Init_2 */ + + /* USER CODE END MX_GPIO_Init_2 */ +} + +/* USER CODE BEGIN 5 */ + +/* USER CODE END 5 */ + +/** + * @brief Period elapsed callback in non blocking mode + * @note This function is called when TIM6 interrupt took place, inside + * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment + * a global variable "uwTick" used as application time base. + * @param htim : TIM handle + * @retval None + */ +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + /* USER CODE BEGIN Callback 0 */ + + /* USER CODE END Callback 0 */ + if (htim->Instance == TIM6) + { + HAL_IncTick(); + } + /* USER CODE BEGIN Callback 1 */ + + /* USER CODE END Callback 1 */ +} + +/** + * @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(); + + //TODO: check if it is necessary to stop FreeRTOS + + uint32_t error_code_can1; + uint32_t error_code_can2; + + if (HAL_CAN_GetState(&hcan1) != HAL_CAN_STATE_READY) error_code_can1 = HAL_CAN_GetError(&hcan1); + if (HAL_CAN_GetState(&hcan2) != HAL_CAN_STATE_READY) error_code_can2 = HAL_CAN_GetError(&hcan2); + + // TODO: write error to SD and/or serial + + HAL_GPIO_WritePin(led_can1.port, led_can1.pin, GPIO_PIN_RESET); + HAL_GPIO_WritePin(led_can2.port, led_can2.pin, GPIO_PIN_RESET); + + while (1) + { + HAL_GPIO_TogglePin(led_error.port, led_error.pin); + HAL_Delay(250); + } + /* 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 */ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index b013ffc..0000000 --- a/LICENSE +++ /dev/null @@ -1,38 +0,0 @@ -GNU GENERAL PUBLIC LICENSE -Version 3, 29 June 2007 - -Copyright (C) 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -Preamble - -The GNU General Public License is a free, copyleft license for -software and other kinds of works. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights.