printf("ho_tari\n");

5일차 본문

2025.03.31

 

오늘의 학습 목표

1. 과제 review

2. UART RX Int Enqueue / Dequeue

3. Timer INT

 

포인터

- 다른 변수를 가리키는 변수

- 다른 변수의 주소를 담는 변수

 

- *p는 p가 가리키는 것을 의미

- something pointed by p is integer type

- 변수 p가 어떤 주소 값을 담고 있는데 그 주소를 따라가면 다른 변수가 있고 그 변수는 타입이 정수다.

- 일반적인 변수 값을 담는 그릇이 아니라 주소 값을 담는 그릇

 

포인터 활용

- 간접 참조 또는 참조 (Dereferencing, Indirection)

- *p = 5; 이면 num에 간접 접근

- num = 5; 이면 num에 직접 접근

 

포인터.ppt
0.56MB

 

 

UART 통신을 이용하여 DS1302에서의 RTC값 보정

<main.c>

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "extern.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 ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
  .name = "myTask02",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
  .name = "myTask03",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; // uart2 rx byte
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__    // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE   // Add for printf
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART3 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}
/* 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 */
  HAL_UART_Receive_IT(&huart2, &rx_data, 1);
  // led_main();
  ds1302_main();
  /* 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 */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of myTask02 */
  myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);

  /* creation of myTask03 */
  myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);

  /* 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)
  {
//	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
//	  HAL_Delay(500);
//	  HAL_GPIO_WritePin(GPIOB, 0xff, 1);
//	  HAL_Delay(500);
//	  HAL_GPIO_WritePin(GPIOB, 0xff, 0);
//	  HAL_Delay(500);
//	  button_led_toggle_test();
    /* 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_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != 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 = 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;
  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 Output Level */
  HAL_GPIO_WritePin(GPIOA, LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, 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_RESET);

  /*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 pins : PC0 PC1 PC2 PC3 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : LD2_Pin CE_DS1302_Pin IO_DS1302_Pin CLK_DS1302_Pin */
  GPIO_InitStruct.Pin = LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB0 PB1 PB2 PB3
                           PB4 PB5 PB6 PB7 */
  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_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 4 */

/* USER CODE END 4 */

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	osDelay(50);
  }
  /* USER CODE END 5 */
}

/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
	button_led_toggle_test();
    osDelay(1);
  }
  /* USER CODE END StartTask02 */
}

/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
  /* USER CODE BEGIN StartTask03 */
  /* Infinite loop */
  for(;;)
  {
	osDelay(1);
  }
  /* USER CODE END StartTask03 */
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM10 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 == TIM10) {
    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 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

<uart.h>

/*
 * uart.h
 *
 *  Created on: Mar 31, 2025
 *      Author: microsoft
 */

#ifndef INC_UART_H_
#define INC_UART_H_

#include "main.h"

#define COMMAND_NUMBER 20
#define COMMAND_LENGTH 40

// led_all_on\n
// led_all_off\n
volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
volatile int rear = 0; // input index : HAL_UART_RxCpltCallback에서 집어 넣어주는 index
volatile int front = 0; // output index

#endif /* INC_UART_H_ */

 

<uart.c>

/*
 * uart.c
 *
 *  Created on: Mar 31, 2025
 *      Author: microsoft
 */
#include <string.h>

#include "uart.h"
#include "extern.h"

void pc_command_processing(void);

/**
  * @brief  Rx Transfer completed callbacks.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  *
  * move from Drivers/STM32F4xx_HAL_driver/Src/stm32f4xx_hal_uart.c to here
  * 예) comportmaster로부터 1char를 수신하면 HAL_UART_RxCpltCallback으로 진입
  *     9600bps인 경우 HAL_UART_RxCpltCallback을 수행 후 1ms이내에는 빠져나가야한다.
  *   115200bps                                     86us
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	volatile static int i = 0;

	if(huart == &huart2)
	{
		if(rx_data == '\n')
		{
			rx_buff[rear++][i] = '\0';
			rear %= COMMAND_NUMBER; // 0~9
			i = 0; // 다음 string을 저장하기 위한 1차원 index값을 0으로
			// !!!! rx_buff queue full check하는 logic 추가
		}
		else
		{
			rx_buff[rear][i++] = rx_data;
			// COMMAND_LENGTH를 check하는 logic 추가
		}
		HAL_UART_Receive_IT(&huart2, &rx_data, 1); // 주의: 반드시 집어 넣어야 다음 INT가 발생
	}
}

void pc_command_processing(void)
{
   if (front != rear) // rx_buff에 data가 존재
   {
      printf("%s\n", rx_buff[front]);   // &rx_buff[front][0]
      if (strncmp((const char *)rx_buff[front], (const char *)"led_all_on", strlen("led_all_on")) == NULL)
      {
         printf("find : led_all_on\n");
      }
      else if(strncmp((const char *)rx_buff[front], (const char *)"setrtc", strlen("setrtc")) == NULL)
      {
    	  set_rtc((char *)rx_buff[front] + 6);
      }
      front++;
      front %= COMMAND_NUMBER;
      // !!!! QUEUE full check하는 logic이 들어가야 한다. !!!!
   }
}

 

<ds1302.c>

/*
 * ds1302.c
 *
 *  Created on: Mar 28, 2025
 *      Author: microsoft
 */

#include "ds1302.h"

t_ds1302 ds;

void ds1302_main(void)
{
	init_date_time(&ds);
	init_gpio_ds1302();
	init_ds1302(&ds);

	while(1)
	{
		read_time_ds1302(&ds);
		read_date_ds1302(&ds);
		pc_command_processing();
		// 날짜와 시각을 출력
		printf(" %4d-%2d-%2d %2d:%2d:%2d\n",
					ds.year + 2000,
					ds.month,
					ds.date,
					ds.hours,
					ds.minutes,
					ds.seconds);

		HAL_Delay(1000);
	}
}

void read_time_ds1302(t_ds1302 *ds)
{
	ds->seconds = read_ds1302(ADDR_SECONDS);
	ds->minutes = read_ds1302(ADDR_MINUTES);
	ds->hours = read_ds1302(ADDR_HOURS);
}

void read_date_ds1302(t_ds1302 *ds)
{
	ds->date = read_ds1302(ADDR_DATE);
	ds->month = read_ds1302(ADDR_MONTH);
	ds->dayofweek = read_ds1302(ADDR_DAYOFWEEK);
	ds->year = read_ds1302(ADDR_YEAR);
}

uint8_t read_ds1302(uint8_t addr)
{
	unsigned char data8bits = 0; // 1bit씩 넘어온 것을 담을 그릇(변수)
	// 1. CE high
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	GPIOA_ODR |= 1 << 10;
	// 2. addr 전송
	tx_ds1302(addr + 1); // read addr
	// 3. data를 읽어 들인다.
	rx_ds1302(&data8bits);
	// 4. CE low
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 10);
	// 5. return (bcd to dec )
	return (bcd2dec(data8bits));
}

// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
//     7654 3210
//     0010 0101
//       2   5
//  +  x10  x1
//  ==============
//          25
uint8_t bcd2dec(uint8_t bcd)
{
	uint8_t high, low;

	low = bcd & 0x0f;
	high = (bcd >> 4) * 10; // 00100101 bcd >> 4 ==> 000000100 x 10

	return (high + low);
}

// dec --> bcd
// 예) 25
//   dec           bcd
// 00011001     0010 0101
uint8_t dec2bcd(uint8_t dec)
{
	uint8_t high, low;

	high = (dec / 10) << 4;
	low = dec % 10;

	return (high + low);
}

void rx_ds1302(unsigned char *pdata)
{
	unsigned char temp = 0;
	// IO 포트를 입력 모드로 전환
	input_dataline_ds1302();
	// DS1302로부터 들어온 bit를 LSB부터 8bit를 받아서 temp변수에 저장
	for (int i = 0; i < 8; i++)
	{
		// 1bit를 읽어 들인다.
		//if (HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))
		if (GPIOA_IDR & (1 << 11))
		{
			// 1의 조건만 set
			temp |= 1 << i;
		}
		if (i != 7)
		{
			clock_ds1302();
		}
	}
	*pdata = temp;
}

void init_ds1302(t_ds1302 *ds)
{
	write_ds1302(ADDR_SECONDS, ds->seconds);
	write_ds1302(ADDR_MINUTES, ds->minutes);
	write_ds1302(ADDR_HOURS, ds->hours);
	write_ds1302(ADDR_DATE, ds->date);
	write_ds1302(ADDR_MONTH, ds->month);
	write_ds1302(ADDR_DAYOFWEEK, ds->dayofweek);
	write_ds1302(ADDR_YEAR, ds->year);
}

void write_ds1302(uint8_t addr, uint8_t data)
{
	// 1. CE low --> high
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	GPIOA_ODR |= 1 << 10;
	// 2. addr 전송
	tx_ds1302(addr);
	// 3. data 전송
	tx_ds1302(dec2bcd(data));
	// 4. CE high --> low
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 10);
}

void tx_ds1302(uint8_t value)
{
	output_dataline_ds1302();
	// 예) addr 초를 write하는
	// 80h M       L
	//     1000 0000   실제값 (B0를 전송시의 )
	//     0000 0001 &
	//     0000 0000  ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);

	//     1000 0000   실제값 (B7을 전송시의 )
	//     1000 0000 &
	//     1000 0000  ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	for (int i  = 0; i < 8; i++)
	{
		if (value & (1 << i))
		{
			// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
			GPIOA_ODR |= 1 << 11;
		}
		else
		{
			// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
			GPIOA_ODR &= ~(1 << 11);
		}
		clock_ds1302();
	}
}

void input_dataline_ds1302(void)
{
//	GPIO_InitTypeDef GPIO_init = {0, };

//	GPIO_init.Pin = IO_DS1302_Pin;
//	GPIO_init.Mode = GPIO_MODE_INPUT; // input mode
//	GPIO_init.Pull = GPIO_NOPULL;
//	HAL_GPIO_Init(GPIOA, &GPIO_init);
	GPIOA_ODR &= ~(1 << 11);
	GPIOA_MODER &= ~(0b11 << 22);
	GPIOA_PUPDR &= ~(0b11 << 22);
}

void output_dataline_ds1302(void)
{
//	GPIO_InitTypeDef GPIO_init = {0, };

//	GPIO_init.Pin = IO_DS1302_Pin;
//	GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // output mode
//	GPIO_init.Pull = GPIO_NOPULL;
//	GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW: 2MHz, HIGH: 25~100MHz
//	HAL_GPIO_Init(GPIOA, &GPIO_init);
	GPIOA_ODR &= ~(1 << 11);
	GPIOA_MODER |= 0b1 << 22;
	GPIOA_MODER &= ~(0b1 << 23);
	GPIOA_OTYPER &= ~(0b1 << 11);
	GPIOA_PUPDR &= ~(0b11 << 22);
	GPIOA_OSPEEDR |= 0b11 << 22;
}

void clock_ds1302(void)
{
	// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
	GPIOA_ODR |= 1 << 12;
	// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 12);
}

void init_gpio_ds1302(void)
{
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 10);
	// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 11);
	// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 12);
}

void init_date_time(t_ds1302 *ds)
{
	ds->year = 25;
	ds->month = 3;
	ds->date = 28;
	ds->dayofweek  = 6; // Fri
	ds->hours = 11;
	ds->minutes = 30;
	ds->seconds = 00;
}

void set_rtc(char *date_time) // YYMMDDHHmmSS
{
	char buf[3] = {0}; // 두자리 문자열과 null 저장 위해 3바이트 배열
	uint8_t year, month, date, hours, minutes, seconds;

	// 입력된 date_time 문자열에서 각 날짜/시간 분리해서 정수형 값으로 변환
	strncpy(buf, date_time, 2); // strncpy로 date_time의 처음 문자 2개를 buf에 복사
	ds.year = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 year에 저장

	strncpy(buf, date_time + 2, 2); // date_time + 2 위치에서의 문자 2개 buf에 복사
	ds.month = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 month에 저장

	strncpy(buf, date_time + 4, 2); // date_time + 4 위치에서의 문자 2개 buf에 복사
	ds.date = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 date에 저장

	strncpy(buf, date_time + 6, 2); // date_time + 6 위치에서의 문자 2개 buf에 복사
	ds.hours = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 hours에 저장

	strncpy(buf, date_time + 8, 2); // date_time + 8 위치에서의 문자 2개 buf에 복사
	ds.minutes = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 minutes에 저장

	strncpy(buf, date_time + 10, 2); // date_time + 10 위치에서의 문자 2개 buf에 복사
	ds.seconds = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 seconds에 저장

	// 해당 레지스터에 기록하기 위해 write_ds1302 함수 호출
	init_ds1302(&ds);
}

 

<실행 결과>

https://youtu.be/71vo021KjWM

 

타이머 / 인터럽트 사용

<main.c>

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "extern.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 ---------------------------------------------------------*/
TIM_HandleTypeDef htim11;

UART_HandleTypeDef huart2;

/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
  .name = "myTask02",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
  .name = "myTask03",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; // uart2 rx byte
volatile int TIM11_1ms_counter = 0; // timer11의 1ms 카운터
/* 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_TIM11_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__    // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE   // Add for printf
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART3 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}
/* 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_TIM11_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2, &rx_data, 1);
  HAL_TIM_Base_Start_IT(&htim11);
  // led_main();
  ds1302_main();
  /* 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 */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of myTask02 */
  myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);

  /* creation of myTask03 */
  myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);

  /* 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)
  {
//	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
//	  HAL_Delay(500);
//	  HAL_GPIO_WritePin(GPIOB, 0xff, 1);
//	  HAL_Delay(500);
//	  HAL_GPIO_WritePin(GPIOB, 0xff, 0);
//	  HAL_Delay(500);
//	  button_led_toggle_test();
    /* 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_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/**
  * @brief TIM11 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM11_Init(void)
{

  /* USER CODE BEGIN TIM11_Init 0 */

  /* USER CODE END TIM11_Init 0 */

  /* USER CODE BEGIN TIM11_Init 1 */

  /* USER CODE END TIM11_Init 1 */
  htim11.Instance = TIM11;
  htim11.Init.Prescaler = 84-1;
  htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim11.Init.Period = 1000-1;
  htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM11_Init 2 */

  /* USER CODE END TIM11_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;
  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 Output Level */
  HAL_GPIO_WritePin(GPIOA, LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, 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_RESET);

  /*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 pins : PC0 PC1 PC2 PC3 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : LD2_Pin CE_DS1302_Pin IO_DS1302_Pin CLK_DS1302_Pin */
  GPIO_InitStruct.Pin = LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB0 PB1 PB2 PB3
                           PB4 PB5 PB6 PB7 */
  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_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 4 */

/* USER CODE END 4 */

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	osDelay(50);
  }
  /* USER CODE END 5 */
}

/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
	button_led_toggle_test();
    osDelay(1);
  }
  /* USER CODE END StartTask02 */
}

/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
  /* USER CODE BEGIN StartTask03 */
  /* Infinite loop */
  for(;;)
  {
	osDelay(1);
  }
  /* USER CODE END StartTask03 */
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM10 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 == TIM10) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
  if (htim->Instance == TIM11) {
	  TIM11_1ms_counter++;
  }
  /* 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 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

<ds1302.c>

/*
 * ds1302.c
 *
 *  Created on: Mar 28, 2025
 *      Author: microsoft
 */

#include "ds1302.h"
#include "extern.h"

t_ds1302 ds;

void ds1302_main(void)
{
	init_date_time(&ds);
	init_gpio_ds1302();
	init_ds1302(&ds);

	while(1)
	{
		read_time_ds1302(&ds);
		read_date_ds1302(&ds);
		pc_command_processing();
		if(TIM11_1ms_counter > 1000)
		{
			TIM11_1ms_counter = 0;
			// 날짜와 시각을 출력
			printf(" %4d-%2d-%2d %2d:%2d:%2d\n",
						ds.year + 2000,
						ds.month,
						ds.date,
						ds.hours,
						ds.minutes,
						ds.seconds);

			// HAL_Delay(1000);
		}
	}
}

void read_time_ds1302(t_ds1302 *ds)
{
	ds->seconds = read_ds1302(ADDR_SECONDS);
	ds->minutes = read_ds1302(ADDR_MINUTES);
	ds->hours = read_ds1302(ADDR_HOURS);
}

void read_date_ds1302(t_ds1302 *ds)
{
	ds->date = read_ds1302(ADDR_DATE);
	ds->month = read_ds1302(ADDR_MONTH);
	ds->dayofweek = read_ds1302(ADDR_DAYOFWEEK);
	ds->year = read_ds1302(ADDR_YEAR);
}

uint8_t read_ds1302(uint8_t addr)
{
	unsigned char data8bits = 0; // 1bit씩 넘어온 것을 담을 그릇(변수)
	// 1. CE high
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	GPIOA_ODR |= 1 << 10;
	// 2. addr 전송
	tx_ds1302(addr + 1); // read addr
	// 3. data를 읽어 들인다.
	rx_ds1302(&data8bits);
	// 4. CE low
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 10);
	// 5. return (bcd to dec )
	return (bcd2dec(data8bits));
}

// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
//     7654 3210
//     0010 0101
//       2   5
//  +  x10  x1
//  ==============
//          25
uint8_t bcd2dec(uint8_t bcd)
{
	uint8_t high, low;

	low = bcd & 0x0f;
	high = (bcd >> 4) * 10; // 00100101 bcd >> 4 ==> 000000100 x 10

	return (high + low);
}

// dec --> bcd
// 예) 25
//   dec           bcd
// 00011001     0010 0101
uint8_t dec2bcd(uint8_t dec)
{
	uint8_t high, low;

	high = (dec / 10) << 4;
	low = dec % 10;

	return (high + low);
}

void rx_ds1302(unsigned char *pdata)
{
	unsigned char temp = 0;
	// IO 포트를 입력 모드로 전환
	input_dataline_ds1302();
	// DS1302로부터 들어온 bit를 LSB부터 8bit를 받아서 temp변수에 저장
	for (int i = 0; i < 8; i++)
	{
		// 1bit를 읽어 들인다.
		//if (HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))
		if (GPIOA_IDR & (1 << 11))
		{
			// 1의 조건만 set
			temp |= 1 << i;
		}
		if (i != 7)
		{
			clock_ds1302();
		}
	}
	*pdata = temp;
}

void init_ds1302(t_ds1302 *ds)
{
	write_ds1302(ADDR_SECONDS, ds->seconds);
	write_ds1302(ADDR_MINUTES, ds->minutes);
	write_ds1302(ADDR_HOURS, ds->hours);
	write_ds1302(ADDR_DATE, ds->date);
	write_ds1302(ADDR_MONTH, ds->month);
	write_ds1302(ADDR_DAYOFWEEK, ds->dayofweek);
	write_ds1302(ADDR_YEAR, ds->year);
}

void write_ds1302(uint8_t addr, uint8_t data)
{
	// 1. CE low --> high
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	GPIOA_ODR |= 1 << 10;
	// 2. addr 전송
	tx_ds1302(addr);
	// 3. data 전송
	tx_ds1302(dec2bcd(data));
	// 4. CE high --> low
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 10);
}

void tx_ds1302(uint8_t value)
{
	output_dataline_ds1302();
	// 예) addr 초를 write하는
	// 80h M       L
	//     1000 0000   실제값 (B0를 전송시의 )
	//     0000 0001 &
	//     0000 0000  ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);

	//     1000 0000   실제값 (B7을 전송시의 )
	//     1000 0000 &
	//     1000 0000  ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	for (int i  = 0; i < 8; i++)
	{
		if (value & (1 << i))
		{
			// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
			GPIOA_ODR |= 1 << 11;
		}
		else
		{
			// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
			GPIOA_ODR &= ~(1 << 11);
		}
		clock_ds1302();
	}
}

void input_dataline_ds1302(void)
{
//	GPIO_InitTypeDef GPIO_init = {0, };

//	GPIO_init.Pin = IO_DS1302_Pin;
//	GPIO_init.Mode = GPIO_MODE_INPUT; // input mode
//	GPIO_init.Pull = GPIO_NOPULL;
//	HAL_GPIO_Init(GPIOA, &GPIO_init);
	GPIOA_ODR &= ~(1 << 11);
	GPIOA_MODER &= ~(0b11 << 22);
	GPIOA_PUPDR &= ~(0b11 << 22);
}

void output_dataline_ds1302(void)
{
//	GPIO_InitTypeDef GPIO_init = {0, };

//	GPIO_init.Pin = IO_DS1302_Pin;
//	GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // output mode
//	GPIO_init.Pull = GPIO_NOPULL;
//	GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW: 2MHz, HIGH: 25~100MHz
//	HAL_GPIO_Init(GPIOA, &GPIO_init);
	GPIOA_ODR &= ~(1 << 11);
	GPIOA_MODER |= 0b1 << 22;
	GPIOA_MODER &= ~(0b1 << 23);
	GPIOA_OTYPER &= ~(0b1 << 11);
	GPIOA_PUPDR &= ~(0b11 << 22);
	GPIOA_OSPEEDR |= 0b11 << 22;
}

void clock_ds1302(void)
{
	// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
	GPIOA_ODR |= 1 << 12;
	// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 12);
}

void init_gpio_ds1302(void)
{
	// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 10);
	// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 11);
	// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
	GPIOA_ODR &= ~(1 << 12);
}

void init_date_time(t_ds1302 *ds)
{
	ds->year = 25;
	ds->month = 3;
	ds->date = 28;
	ds->dayofweek  = 6; // Fri
	ds->hours = 11;
	ds->minutes = 30;
	ds->seconds = 00;
}

void set_rtc(char *date_time) // setrtcYYMMDDHHmmSS
{
	char buf[3] = {0}; // 두자리 문자열과 null 저장 위해 3바이트 배열
	uint8_t year, month, date, hours, minutes, seconds;

	// 입력된 date_time 문자열에서 각 날짜/시간 분리해서 정수형 값으로 변환
	strncpy(buf, date_time, 2); // strncpy로 date_time의 처음 문자 2개를 buf에 복사
	ds.year = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 year에 저장

	strncpy(buf, date_time + 2, 2); // date_time + 2 위치에서의 문자 2개 buf에 복사
	ds.month = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 month에 저장

	strncpy(buf, date_time + 4, 2); // date_time + 4 위치에서의 문자 2개 buf에 복사
	ds.date = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 date에 저장

	strncpy(buf, date_time + 6, 2); // date_time + 6 위치에서의 문자 2개 buf에 복사
	ds.hours = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 hours에 저장

	strncpy(buf, date_time + 8, 2); // date_time + 8 위치에서의 문자 2개 buf에 복사
	ds.minutes = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 minutes에 저장

	strncpy(buf, date_time + 10, 2); // date_time + 10 위치에서의 문자 2개 buf에 복사
	ds.seconds = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 seconds에 저장

	// 해당 레지스터에 기록하기 위해 write_ds1302 함수 호출
	init_ds1302(&ds);
}

 

<실행 결과>

https://youtu.be/plaBuF59fYI

 

DHT11 온도 / 습도 측정

<main.c>

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "extern.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 ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim11;

UART_HandleTypeDef huart2;

/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
  .name = "myTask02",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
  .name = "myTask03",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; // uart2 rx byte
volatile int TIM11_1ms_counter = 0; // timer11?�� 1ms 카운?��
/* 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_TIM11_Init(void);
static void MX_TIM2_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__    // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE   // Add for printf
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART3 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}
/* 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_TIM11_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2, &rx_data, 1);
  HAL_TIM_Base_Start_IT(&htim11);
  HAL_TIM_Base_Start_IT(&htim2); // for make delay_us

  dht11_main();
  // led_main();
  // ds1302_main();
  /* 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 */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of myTask02 */
  myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);

  /* creation of myTask03 */
  myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);

  /* 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)
  {
//	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
//	  HAL_Delay(500);
//	  HAL_GPIO_WritePin(GPIOB, 0xff, 1);
//	  HAL_Delay(500);
//	  HAL_GPIO_WritePin(GPIOB, 0xff, 0);
//	  HAL_Delay(500);
//	  button_led_toggle_test();
    /* 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_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/**
  * @brief TIM2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 84-1;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 4294967295;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

/**
  * @brief TIM11 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM11_Init(void)
{

  /* USER CODE BEGIN TIM11_Init 0 */

  /* USER CODE END TIM11_Init 0 */

  /* USER CODE BEGIN TIM11_Init 1 */

  /* USER CODE END TIM11_Init 1 */
  htim11.Instance = TIM11;
  htim11.Init.Prescaler = 84-1;
  htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim11.Init.Period = 1000-1;
  htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM11_Init 2 */

  /* USER CODE END TIM11_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;
  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 Output Level */
  HAL_GPIO_WritePin(GPIOA, DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
                          |CLK_DS1302_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, 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_RESET);

  /*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 pins : PC0 PC1 PC2 PC3 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : DHT11_Pin LD2_Pin CE_DS1302_Pin IO_DS1302_Pin
                           CLK_DS1302_Pin */
  GPIO_InitStruct.Pin = DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
                          |CLK_DS1302_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB0 PB1 PB2 PB3
                           PB4 PB5 PB6 PB7 */
  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_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 4 */

/* USER CODE END 4 */

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	osDelay(50);
  }
  /* USER CODE END 5 */
}

/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
	button_led_toggle_test();
    osDelay(1);
  }
  /* USER CODE END StartTask02 */
}

/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
  /* USER CODE BEGIN StartTask03 */
  /* Infinite loop */
  for(;;)
  {
	osDelay(1);
  }
  /* USER CODE END StartTask03 */
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM10 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 == TIM10) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
  if (htim->Instance == TIM11) {
	  TIM11_1ms_counter++;
  }
  /* 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 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

<dht11.c>

#include "dht11.h"
#include "extern.h"
#include "timer.h"

// us_count : 마이크로초 단위로 DHT11 신호의 HIGH/LOW 지속시간을 측정하기 위한 변수.
// dht11_state : 센서와의 통신 상태를 나타내며, 정상(OK), 타임아웃(TIMEOUT), 데이터 오류(VALUE_ERROR) 등을 포함.
uint8_t us_count = 0;
enum state_define dht11_state = OK;

void init_dht11(void)
{
//	DHT_DDR |= 1 << DHT_PIN_NUM;	// 데이터 핀(PG0)을 출력 모드로 설정
//	DHT_PORT |= 1 << DHT_PIN_NUM;	// 출력을 HIGH로 유지하여 기본 풀업 상태를 만듦

	GPIOA_MODER &= ~(0b11 << 0);
	GPIOA_MODER |= 0b01 << 0;
	GPIOA_ODR |= 1 << 0;
}

void dht11_main(void)
{
	// 센서에서 수신한 5바이트 데이터 배열:
	// [0]: 습도 정수, [1]: 습도 소수, [2]: 온도 정수, [3]: 온도 소수, [4]: 체크섬
	uint8_t data[5] = { 0, };

	init_dht11();

	while (1)
	{
		memset(data, 0, sizeof(data));	// 데이터 배열 초기화
		dht11_state = OK;

		// ===== [Step 1: Start Signal] =====
		init_dht11();		// 데이터 라인을 HIGH로 초기화 (풀업 상태)
		delay_us(100000);		// 안정화를 위해 대기

		// DHT_PORT &= ~(1 << DHT_PIN_NUM);	// 데이터 라인을 LOW로 내려서 시작 신호 전송
		GPIOA_ODR &= ~(1 << 0);
		delay_us(20000);						// 최소 datasheet 권장 18ms 이상 LOW 유지 (여기서는 20ms)

//		DHT_PORT |= 1 << DHT_PIN_NUM;		// 다시 HIGH로 전환
//		DHT_DDR &= ~(1 << DHT_PIN_NUM);		// 핀을 입력 모드로 전환하여 센서 응답 수신
		GPIOA_ODR |= 1 << 0;
		GPIOA_MODER &= ~(0b11 << (0 * 2));
		delay_us(1);						// 짧은 지연

		 // ===== [Step 2: Sensor Response Check Part 1] =====
		 // 센서가 시작 신호에 대해 LOW 응답을 시작하기 전까지 HIGH 상태가 지속되면 에러 처리.
		us_count = 0;
		// while ( (DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM )	// 아직도 HIGH인지 check
		while (((GPIOA_IDR & (1 << 0)) >> 0))
		{
			delay_us(2);
			us_count += 2;
			if (us_count > 50)	// 50us 이상 HIGH가 지속되면 타임아웃 발생
			{
				dht11_state = TIMEOUT;
				break;
			}
		}

		// ===== [Step 2: Sensor Response Check Part 2] =====
		// 센서가 LOW 상태로 전환되었으면, LOW 유지 기간(약 80us, 여기서는 여유를 두어 100us) 체크
		if (dht11_state == OK)
		{
			us_count = 0;
			// while ( !((DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM) )
			while ( !((GPIOA_IDR & (1 << 0)) >> 0))
			{
				delay_us(2);
				us_count += 2;
				if (us_count > 100)	// LOW 신호가 100us 이상 지속되지 않으면 타임아웃
				{
					dht11_state = TIMEOUT;
					break;
				}
			}
		}

		// ===== [Step 2: Sensor Response Check Part 3] =====
		// 이후 센서가 HIGH 상태로 전환되면, HIGH 유지 시간(약 80us, 여유를 두어 100us) 체크
		if (dht11_state == OK)
		{
			us_count = 0;
			// while ( (DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM )
			while ( ((GPIOA_IDR & (1 << 0)) >> 0))
			{
				delay_us(2);
				us_count += 2;
				if (us_count > 100)	// HIGH 신호가 너무 길면 타임아웃 처리
				{
					dht11_state = TIMEOUT;
					break;
				}
			}
		}
		// 이 시점에서 dht11_state가 OK이면 시작 신호와 센서 응답이 정상적으로 이루어짐.

		// ===== [Step 3: Data Bit Reception] =====
		// 센서가 40비트 데이터를 순차적으로 전송합니다.
		// 각 비트 전송:
		//   - 50us LOW 펄스로 시작한 후 HIGH 펄스 길이로 비트 값 결정
		//       * HIGH 펄스가 약 26~28us이면 '0'
		//       * HIGH 펄스가 약 70us이면 '1'
		// 코드에서는 HIGH 펄스 지속시간이 40us 미만이면 '0', 40us 이상이면 '1'로 판정합니다.
		if (dht11_state == OK)
		{
			for (int i = 0; i < 5; i++) // 5바이트 데이터 수신
			{
				uint8_t pulse[8] = {0, };
				// 한 바이트는 8비트로 구성 (MSB부터 LSB 순으로 읽음)
				for (int j = 7; j >= 0; j--)
				{
					 // --- LOW 펄스 체크 (각 비트 시작: 약 50us) ---
					us_count = 0;
					// while ( !((DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM) )
					while ( !((GPIOA_IDR & (1 << 0) >> 0)))
					{
						delay_us(2);
						us_count += 2;
						if (us_count > 70)	// LOW 펄스가 spec(50us)보다 현저히 길면 에러 처리 (여유를 두어 70us)
						{
							dht11_state = TIMEOUT;
							i = 5;	// 바깥 for 문 탈출을 위한 변수 재설정
							j = -1;
							break;
						}
					}

					// --- HIGH 펄스 체크 ---
					// LOW 펄스 종료 후, HIGH 펄스의 길이를 측정하여 비트 '0' 또는 '1'을 결정
					if (dht11_state == OK)
					{
						us_count = 0;
						// while ( (DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM )	// HIGH 일동안 반복
						while ( ((GPIOA_IDR & (1 << 0)) >> 0))
						{
							delay_us(2);
							us_count += 2;
							if (us_count > 90)	// HIGH 펄스가 90us 이상이면 타임아웃 (비트 '1'은 약 70us, 여유 고려)
							{
								dht11_state = TIMEOUT;
								i = 5;
								j = -1;		// for문 전체 탈출
								break;
							}
						}
					}
					// HIGH 펄스의 길이에 따라 비트 값을 결정: 40us 미만이면 '0', 이상이면 '1'
					if (dht11_state == OK)
					{
						if (us_count < 40) {
							pulse[j] = 0;
						}
						else {
							pulse[j] = 1;
						}
					}
				}

				// 한 바이트의 8비트가 정상적으로 읽혔다면 pulse 배열을 하나의 바이트로 변환
				if (dht11_state == OK)	// pulse를 40개를 정상 처리했으면
				{
					data[i] = pulse[0] << 0 | pulse[1] << 1 | pulse[2] << 2 | pulse[3] << 3 |
							  pulse[4] << 4 | pulse[5] << 5 | pulse[6] << 6 | pulse[7] << 7;
				}
			}

			// ===== [Step 4: Checksum 검증] =====
			// 전송된 5번째 바이트(data[4])는 체크섬으로, 앞 4바이트의 합과 같아야 합니다.
			if (dht11_state == OK)
			{
				if (data[4] != data[0] + data[1] + data[2] + data[3])
				{
					dht11_state = VALUE_ERROR;
				}
			}
			delay_us(50);	// 데이터 전송 후 약간의 여유 시간 (spec: 50us, 여기서는 60us 여유)
		}

		// ===== [Step 5: 결과 출력 및 안정화 대기] =====
		if (dht11_state == OK)
		{
			printf("temperature : %d.%d\n", data[2], data[3]);
			printf("humidity : %d.%d\n", data[0], data[1]);
		}
		else
		{
			printf("error code : %d\n", dht11_state);
		}
		delay_us(2000000);	// 센서 안정화 및 다음 측정을 위한 대기 시간

	}
}

 

<dht11.h>

/*
 * dht11.h
 *
 *  Created on: Mar 31, 2025
 *      Author: microsoft
 */

#ifndef INC_DHT11_H_
#define INC_DHT11_H_

#include "main.h"

#define GPIOA_MODER *(unsigned int *)0x40020000
#define GPIOA_OTYPER *(unsigned int *)0x40020004
#define GPIOA_ODR *(unsigned int *)0x40020014
#define GPIOA_IDR *(unsigned int *)0x40020010
#define GPIOA_PUPDR *(unsigned int *)0x4002000C
#define GPIOA_BSRR *(unsigned int *)0x40020018
#define GPIOA_OSPEEDR *(unsigned int *)0x40020008

enum state_define{
    OK,
    TIMEOUT,
    VALUE_ERROR,
    TRANS_ERROR
};
extern enum state_define dht11_state;

// 함수 선언
void init_dht11(void);
void dht11_main(void);
void signal_us_check(int duration, int binary_choice);
int dht11_get_bit(void);

extern uint8_t us_count;

#endif /* INC_DHT11_H_ */

 

<실행 결과>

https://youtube.com/shorts/4xUFONrn_7s

 

'(Telechips) AI 시스템 반도체 SW 개발자 교육 > STM32CubeIDE' 카테고리의 다른 글

7일차  (0) 2025.04.02
6일차  (0) 2025.04.01
4일차  (0) 2025.03.28
3일차  (0) 2025.03.27
2일차  (0) 2025.03.26