A PC fan controller that doesn't (completely) suck.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

468 lines
12 KiB

  1. /* USER CODE BEGIN Header */
  2. /**
  3. ******************************************************************************
  4. * @file : main.c
  5. * @brief : Main program body
  6. ******************************************************************************
  7. * @attention
  8. *
  9. * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  10. * All rights reserved.</center></h2>
  11. *
  12. * This software component is licensed by ST under BSD 3-Clause license,
  13. * the "License"; You may not use this file except in compliance with the
  14. * License. You may obtain a copy of the License at:
  15. * opensource.org/licenses/BSD-3-Clause
  16. *
  17. ******************************************************************************
  18. */
  19. /* USER CODE END Header */
  20. /* Includes ------------------------------------------------------------------*/
  21. #include "main.h"
  22. /* Private includes ----------------------------------------------------------*/
  23. /* USER CODE BEGIN Includes */
  24. #include "string.h"
  25. #include "stdio.h"
  26. /* USER CODE END Includes */
  27. /* Private typedef -----------------------------------------------------------*/
  28. /* USER CODE BEGIN PTD */
  29. /* USER CODE END PTD */
  30. /* Private define ------------------------------------------------------------*/
  31. /* USER CODE BEGIN PD */
  32. #define TIMEBASE 1000
  33. #define PWM_MINIMUM 20
  34. /* USER CODE END PD */
  35. /* Private macro -------------------------------------------------------------*/
  36. /* USER CODE BEGIN PM */
  37. /* USER CODE END PM */
  38. /* Private variables ---------------------------------------------------------*/
  39. TIM_HandleTypeDef htim2;
  40. UART_HandleTypeDef huart2;
  41. /* USER CODE BEGIN PV */
  42. const uint16_t GPU_STAGES[] = {900, 1400, 1800, 2200};
  43. const uint16_t CPU_STAGES[] = {850, 900, 1000, 1200};
  44. uint32_t freq_count_gpu;
  45. uint32_t freq_count_cpu;
  46. uint32_t freq_count_case1;
  47. uint32_t freq_count_case2;
  48. uint32_t last_gpu;
  49. uint32_t last_cpu;
  50. uint32_t last_case1;
  51. uint32_t last_case2;
  52. uint16_t rpm_gpu;
  53. uint16_t rpm_cpu;
  54. uint16_t rpm_case1;
  55. uint16_t rpm_case2;
  56. uint32_t pwm_fan1;
  57. uint32_t pwm_fan2;
  58. uint8_t gpu_stage;
  59. uint8_t cpu_stage;
  60. uint8_t current_stage;
  61. char msg[256];
  62. /* USER CODE END PV */
  63. /* Private function prototypes -----------------------------------------------*/
  64. void SystemClock_Config(void);
  65. static void MX_GPIO_Init(void);
  66. static void MX_TIM2_Init(void);
  67. static void MX_USART2_UART_Init(void);
  68. /* USER CODE BEGIN PFP */
  69. /* USER CODE END PFP */
  70. /* Private user code ---------------------------------------------------------*/
  71. /* USER CODE BEGIN 0 */
  72. int max(uint8_t a, uint8_t b) {
  73. if (a > b) {
  74. return a;
  75. }
  76. return b;
  77. }
  78. /* USER CODE END 0 */
  79. /**
  80. * @brief The application entry point.
  81. * @retval int
  82. */
  83. int main(void)
  84. {
  85. /* USER CODE BEGIN 1 */
  86. /* USER CODE END 1 */
  87. /* MCU Configuration--------------------------------------------------------*/
  88. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  89. HAL_Init();
  90. /* USER CODE BEGIN Init */
  91. /* USER CODE END Init */
  92. /* Configure the system clock */
  93. SystemClock_Config();
  94. /* USER CODE BEGIN SysInit */
  95. /* USER CODE END SysInit */
  96. /* Initialize all configured peripherals */
  97. MX_GPIO_Init();
  98. MX_TIM2_Init();
  99. MX_USART2_UART_Init();
  100. /* USER CODE BEGIN 2 */
  101. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  102. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
  103. /* USER CODE END 2 */
  104. /* Infinite loop */
  105. /* USER CODE BEGIN WHILE */
  106. while (1)
  107. {
  108. if (HAL_GPIO_ReadPin(Switch_GPIO_Port, Switch_Pin) == GPIO_PIN_SET) {
  109. // do stuff
  110. }
  111. if (rpm_gpu < GPU_STAGES[0]) {
  112. gpu_stage = 1;
  113. } else if (rpm_gpu < GPU_STAGES[1]) {
  114. gpu_stage = 2;
  115. } else if (rpm_gpu < GPU_STAGES[2]) {
  116. gpu_stage = 3;
  117. } else if (rpm_gpu < GPU_STAGES[3]) {
  118. gpu_stage = 4;
  119. } else {
  120. gpu_stage = 5;
  121. }
  122. if (rpm_cpu < CPU_STAGES[0]) {
  123. cpu_stage = 1;
  124. } else if (rpm_cpu < CPU_STAGES[1]) {
  125. cpu_stage = 2;
  126. } else if (rpm_cpu < CPU_STAGES[2]) {
  127. cpu_stage = 3;
  128. } else if (rpm_cpu < CPU_STAGES[3]) {
  129. cpu_stage = 4;
  130. } else {
  131. cpu_stage = 5;
  132. }
  133. current_stage = max(gpu_stage, cpu_stage);
  134. if (current_stage == 1) {
  135. pwm_fan1 = PWM_MINIMUM;
  136. } else if (current_stage == 2) {
  137. pwm_fan1 = 35;
  138. } else if (current_stage == 3) {
  139. pwm_fan1 = 50;
  140. } else if (current_stage == 4) {
  141. pwm_fan1 = 80;
  142. } else {
  143. pwm_fan1 = 100;
  144. }
  145. // For now
  146. pwm_fan2 = pwm_fan1;
  147. // Write the value after converting from duty cycle to raw value.
  148. TIM2->CCR1 = htim2.Init.Period * pwm_fan1 / 100;
  149. TIM2->CCR2 = htim2.Init.Period * pwm_fan2 / 100;
  150. // If the fans don't spin, the rpm won't be updated as no
  151. // interrupts are called. This resets those values every two seconds.
  152. if (HAL_GetTick() - last_gpu >= TIMEBASE * 2) {
  153. rpm_gpu = 0;
  154. freq_count_gpu = 0;
  155. }
  156. if (HAL_GetTick() - last_cpu >= TIMEBASE * 2) {
  157. rpm_cpu = 0;
  158. freq_count_cpu = 0;
  159. }
  160. if (HAL_GetTick() - last_case1 >= TIMEBASE * 2) {
  161. rpm_case1 = 0;
  162. freq_count_case1 = 0;
  163. }
  164. if (HAL_GetTick() - last_case2 >= TIMEBASE * 2) {
  165. rpm_case2 = 0;
  166. freq_count_case2 = 0;
  167. }
  168. sprintf(msg, "GPU: %u RPM\nCPU: %u RPM\nCase 1: %u RPM\nCase2: %u RPM\n", rpm_gpu, rpm_cpu, rpm_case1, rpm_case2);
  169. HAL_UART_Transmit(&huart2, (uint8_t *) msg, strlen(msg), 100);
  170. sprintf(msg, "PWM Case 1: %lu %%\nPWM Case 2: %lu %%\n\n", pwm_fan1, pwm_fan2);
  171. HAL_UART_Transmit(&huart2, (uint8_t *) msg, strlen(msg), 100);
  172. HAL_Delay(1000);
  173. /* USER CODE END WHILE */
  174. /* USER CODE BEGIN 3 */
  175. }
  176. /* USER CODE END 3 */
  177. }
  178. /**
  179. * @brief System Clock Configuration
  180. * @retval None
  181. */
  182. void SystemClock_Config(void)
  183. {
  184. RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  185. RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  186. /** Configure the main internal regulator output voltage
  187. */
  188. HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
  189. /** Initializes the RCC Oscillators according to the specified parameters
  190. * in the RCC_OscInitTypeDef structure.
  191. */
  192. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  193. RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  194. RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
  195. RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  196. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  197. if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  198. {
  199. Error_Handler();
  200. }
  201. /** Initializes the CPU, AHB and APB buses clocks
  202. */
  203. RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  204. |RCC_CLOCKTYPE_PCLK1;
  205. RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  206. RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  207. RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  208. if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  209. {
  210. Error_Handler();
  211. }
  212. }
  213. /**
  214. * @brief TIM2 Initialization Function
  215. * @param None
  216. * @retval None
  217. */
  218. static void MX_TIM2_Init(void)
  219. {
  220. /* USER CODE BEGIN TIM2_Init 0 */
  221. /* USER CODE END TIM2_Init 0 */
  222. TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  223. TIM_MasterConfigTypeDef sMasterConfig = {0};
  224. TIM_OC_InitTypeDef sConfigOC = {0};
  225. /* USER CODE BEGIN TIM2_Init 1 */
  226. /* USER CODE END TIM2_Init 1 */
  227. htim2.Instance = TIM2;
  228. htim2.Init.Prescaler = 0;
  229. htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  230. htim2.Init.Period = 640;
  231. htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  232. htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  233. if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  234. {
  235. Error_Handler();
  236. }
  237. sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  238. if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  239. {
  240. Error_Handler();
  241. }
  242. if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  243. {
  244. Error_Handler();
  245. }
  246. sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  247. sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  248. if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  249. {
  250. Error_Handler();
  251. }
  252. sConfigOC.OCMode = TIM_OCMODE_PWM1;
  253. sConfigOC.Pulse = 0;
  254. sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  255. sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  256. if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  257. {
  258. Error_Handler();
  259. }
  260. if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  261. {
  262. Error_Handler();
  263. }
  264. /* USER CODE BEGIN TIM2_Init 2 */
  265. /* USER CODE END TIM2_Init 2 */
  266. HAL_TIM_MspPostInit(&htim2);
  267. }
  268. /**
  269. * @brief USART2 Initialization Function
  270. * @param None
  271. * @retval None
  272. */
  273. static void MX_USART2_UART_Init(void)
  274. {
  275. /* USER CODE BEGIN USART2_Init 0 */
  276. /* USER CODE END USART2_Init 0 */
  277. /* USER CODE BEGIN USART2_Init 1 */
  278. /* USER CODE END USART2_Init 1 */
  279. huart2.Instance = USART2;
  280. huart2.Init.BaudRate = 115200;
  281. huart2.Init.WordLength = UART_WORDLENGTH_8B;
  282. huart2.Init.StopBits = UART_STOPBITS_1;
  283. huart2.Init.Parity = UART_PARITY_NONE;
  284. huart2.Init.Mode = UART_MODE_TX;
  285. huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  286. huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  287. huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  288. huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  289. huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  290. if (HAL_UART_Init(&huart2) != HAL_OK)
  291. {
  292. Error_Handler();
  293. }
  294. /* USER CODE BEGIN USART2_Init 2 */
  295. /* USER CODE END USART2_Init 2 */
  296. }
  297. /**
  298. * @brief GPIO Initialization Function
  299. * @param None
  300. * @retval None
  301. */
  302. static void MX_GPIO_Init(void)
  303. {
  304. GPIO_InitTypeDef GPIO_InitStruct = {0};
  305. /* GPIO Ports Clock Enable */
  306. __HAL_RCC_GPIOA_CLK_ENABLE();
  307. __HAL_RCC_GPIOB_CLK_ENABLE();
  308. /*Configure GPIO pins : Case1_Pin Case2_Pin CPU_Pin GPU_Pin */
  309. GPIO_InitStruct.Pin = Case1_Pin|Case2_Pin|CPU_Pin|GPU_Pin;
  310. GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  311. GPIO_InitStruct.Pull = GPIO_NOPULL;
  312. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  313. /*Configure GPIO pin : Switch_Pin */
  314. GPIO_InitStruct.Pin = Switch_Pin;
  315. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  316. GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  317. HAL_GPIO_Init(Switch_GPIO_Port, &GPIO_InitStruct);
  318. /* EXTI interrupt init*/
  319. HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);
  320. HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
  321. }
  322. /* USER CODE BEGIN 4 */
  323. void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin) {
  324. if (GPIO_Pin == GPU_Pin) {
  325. if ((HAL_GetTick() - last_gpu) <= TIMEBASE) {
  326. freq_count_gpu++;
  327. } else {
  328. rpm_gpu = freq_count_gpu * 60 / 2;
  329. last_gpu = HAL_GetTick();
  330. freq_count_gpu = 0;
  331. }
  332. }
  333. if (GPIO_Pin == CPU_Pin) {
  334. if ((HAL_GetTick() - last_cpu) <= TIMEBASE) {
  335. freq_count_cpu++;
  336. } else {
  337. rpm_cpu = freq_count_cpu * 60 / 2;
  338. last_cpu = HAL_GetTick();
  339. freq_count_cpu = 0;
  340. }
  341. }
  342. if (GPIO_Pin == Case1_Pin) {
  343. if ((HAL_GetTick() - last_case1) <= TIMEBASE) {
  344. freq_count_case1++;
  345. } else {
  346. rpm_case1 = freq_count_case1 * 60 / 2;
  347. last_case1 = HAL_GetTick();
  348. freq_count_case1 = 0;
  349. }
  350. }
  351. if (GPIO_Pin == Case2_Pin) {
  352. if ((HAL_GetTick() - last_case2) <= TIMEBASE) {
  353. freq_count_case2++;
  354. } else {
  355. rpm_case2 = freq_count_case2 * 60 / 2;
  356. last_case2 = HAL_GetTick();
  357. freq_count_case2 = 0;
  358. }
  359. }
  360. }
  361. /* USER CODE END 4 */
  362. /**
  363. * @brief This function is executed in case of error occurrence.
  364. * @retval None
  365. */
  366. void Error_Handler(void)
  367. {
  368. /* USER CODE BEGIN Error_Handler_Debug */
  369. /* User can add his own implementation to report the HAL error return state */
  370. __disable_irq();
  371. while (1)
  372. {
  373. }
  374. /* USER CODE END Error_Handler_Debug */
  375. }
  376. #ifdef USE_FULL_ASSERT
  377. /**
  378. * @brief Reports the name of the source file and the source line number
  379. * where the assert_param error has occurred.
  380. * @param file: pointer to the source file name
  381. * @param line: assert_param error line source number
  382. * @retval None
  383. */
  384. void assert_failed(uint8_t *file, uint32_t line)
  385. {
  386. /* USER CODE BEGIN 6 */
  387. /* User can add his own implementation to report the file name and line number,
  388. ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  389. /* USER CODE END 6 */
  390. }
  391. #endif /* USE_FULL_ASSERT */
  392. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/