«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Archives
관리 메뉴

printf("ho_tari\n");

6일차 본문

2025.04.01

 

오늘의 학습 목표

1. flash memory handling

2. I2C

3. D FlipFlop → Shift Register

 

STM32F411RE Flash Programming

 

<STM32F411RE Memory MAP>

 

 

- 총 8개의 sector로 구성

- 플래시 메모리는 쓰기전에 먼저 메모리를 지워야 하는데 지울때 사용하는 단위가 sector단위이다.

- 플래시를 지우는 방법은 한 전체를 지우거나 sector 하나를 지정해서 지울 수 있다.

 

Flash 메모리 읽기

- flash 메모리를 읽는 것은 일반 메모리 RAM을 읽는것과 동일 하게 c언어 포인터를 이용 *(읽고자 하는 주소)

- ) 0x08000000 번지에 저장된 4 byte를 읽고자 할 때

uint32_t val = (((uint32_t *) 0x08000000);

 

Flash 메모리 쓰기- 플래시 메모리는 바로 쓸 수 없고 먼저 지운 다음에 쓸 수 있다.- 공장 출하시 flash는 모드 지워진 상태이며 모든 비트가 1로 채워져 있다.- 지우고 나면 flash에는 0xFFFFFFFF로 채워 진다.- 평소 지워지지 않게 lock을 걸어 두고 쓰고자 할때만 UNLOCK하여 사용 - FLASH 쓰기 과정 (1) Unlock : HAL_FLASH_Unlock()(2) Erase : HAL_FLASHEx_Erase() : sector 단위 또는 칩 전체(3) Program : HAL_FLASH_Program() : 쓰는 단위는 1, 2, 4, 8 byte 가능(4) Lock : HAL_FLASH_Lock()

 

Flash programming 문제(1) Alarm 시각을 flash memory에 설정하여 해당 시각이 되면 알람 부저 & Dotmatrix 구동하기(2) 호텔 객실 관리 프로그램을 Doubly Linked list로 구현 후 저장된 data를 Flash에 저장한 후 Power를 껐다 켜면 Flash의 저장 메모리를 읽어서 조회할 수 있도록 program을 구현한다.

 

DS1302 TIMER Flash Memory

<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 ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;

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;

/* 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);
static void MX_I2C1_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();
  MX_I2C1_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

  // i2c_lcd_main();
  ds1302_main();
  //led_main();
  //dht11_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)
  {

//	  button_led_toggle_test();
//	  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);
    /* 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 = 8;
  RCC_OscInitStruct.PLL.PLLN = 84;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  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 I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

/**
  * @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****/

 

<ds1302.c>

#include "ds1302.h"
#include "extern.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

t_ds1302 ds1302;

void ds1302_main(void)
{
	// init_date_time();
	flash_set_time();
	init_gpio_ds1302();
	init_ds1302();
	int TIM11_sec_counter=0;
	//non os
	while(1)
	{
		read_time_ds1302();
		read_date_ds1302();
		pc_command_processing();
		if(TIM11_1ms_counter > 1000)
		{
			TIM11_1ms_counter = 0;
			// flash_main();
//			flash_read((uint32_t *)&ds1302, sizeof(ds1302));
//
//			printf("magic: %08x\n", ds1302.magic);
//			printf("year: %02d\n", ds1302.year);
//			printf("month: %02d\n", ds1302.month);
//			printf("date: %02d\n", ds1302.date);
//			printf("Hours: %02d\n", ds1302.hours);
//			printf("Minutes: %02d\n", ds1302.minutes);
//			printf("Seconds: %02d\n", ds1302.seconds);
			//날짜와 시각을 출력
			if(o_prt.p_rtc)
				printf(" %4d-%02d-%2d %2d:%2d:%02d\n",
						ds1302.year+2000,
						ds1302.month,
						ds1302.date,
						ds1302.hours,
						ds1302.minutes,
						ds1302.seconds);
			TIM11_sec_counter++;
			if (TIM11_sec_counter > 10)
			{
				TIM11_sec_counter=0;
				printf("TIM11_sec_counter > 5000\n");
				printf("magic: %08x\n", ds1302.magic);
				printf("year: %02d\n", ds1302.year);
				printf("month: %02d\n", ds1302.month);
				printf("date: %02d\n", ds1302.date);
				printf("Hours: %02d\n", ds1302.hours);
				printf("Minutes: %02d\n", ds1302.minutes);
				printf("Seconds: %02d\n", ds1302.seconds);
				flash_erase();
				flash_write((uint32_t *)&ds1302, sizeof(ds1302));
			}
		}
	}
}

void read_time_ds1302(void)
{
	ds1302.seconds = read_ds1302(ADDR_SECONDS);
	ds1302.minutes = read_ds1302(ADDR_MINUTES);
	ds1302.hours = read_ds1302(ADDR_HOURS);
}

void read_date_ds1302(void)
{
	ds1302.date = read_ds1302(ADDR_DATE);
	ds1302.month = read_ds1302(ADDR_MONTH);
	ds1302.dayofweek = read_ds1302(ADDR_DAYOFWEEK);
	ds1302.year = read_ds1302(ADDR_YEAR);
}

uint8_t read_ds1302(uint8_t addr)
{
	unsigned char data8bits = 0;	// 1bits 씩 넘어온 것을 담을 그릇 (변수)
	// 1. CE high
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	// 2. send addr
	tx_ds1302(addr + 1);			// read addr
	// 3. read data
	rx_ds1302(&data8bits);
	// 4. CE low
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	// 5. return bcd to dec
	return bcd2dec(data8bits);
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
//    7654 3210
//    0010 0101 --> bcd
//      2    5
//  2*10^1   5*10^0
// =====================
//        25

//dec -> bcd
uint8_t dec2bcd(uint8_t dec)
{
	uint8_t high, low;

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

	return (high + low);
}

//bcd -> dec
uint8_t bcd2dec(uint8_t bcd)
{
	uint8_t high, low;

	low = bcd & 0x0f;
	high = (bcd >> 4) * 10;			// 0010 0101 >> 4 = 0000 0100 * 10

	return (high + low);
}

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

void init_ds1302(void)
{
	write_ds1302(ADDR_SECONDS, ds1302.seconds);
	write_ds1302(ADDR_MINUTES, ds1302.minutes);
	write_ds1302(ADDR_HOURS, ds1302.hours);
	write_ds1302(ADDR_DATE, ds1302.date);
	write_ds1302(ADDR_MONTH, ds1302.month);
	write_ds1302(ADDR_DAYOFWEEK, ds1302.dayofweek);
	write_ds1302(ADDR_YEAR, ds1302.year);
}

void write_ds1302(uint8_t addr, uint8_t data)
{
	// 1. CE low --> high
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	// 2. addr 전송
	tx_ds1302(addr);
	// 3. data 전송
	tx_ds1302(dec2bcd(data));
	// 4. CE high --> low
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);

}

void tx_ds1302(uint8_t addr)
{
	output_dataline_ds1302();
	// 예) addr 초를 write 하는
	// 80h Msb  >  Lsb
	//     1000 0000 실제값
	//     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 (addr & (1 << i))
		{
			HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
		}
		else
		{
			HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
		}
		clock_ds1302();
	}
}

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 on
	GPIO_init.Pull = GPIO_NOPULL;
	GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW : 2M  HIGH : 25 ~ 100MHz
	HAL_GPIO_Init(GPIOA, &GPIO_init);
}

void input_dataline_ds1302(void)
{
	GPIO_InitTypeDef GPIO_init = { 0, };
	GPIO_init.Pin = IO_DS1302_Pin;
	GPIO_init.Mode = GPIO_MODE_INPUT;		// input mode on
	GPIO_init.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOA, &GPIO_init);
}

void clock_ds1302(void)
{
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}

void init_gpio_ds1302(void)
{
	// ALL LOW
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}

void init_date_time(void)
{
	ds1302.magic=0x55555555;  // 사용자가 임의로 설정
	ds1302.year = 25;
	ds1302.month = 4;
	ds1302.date = 1;
	ds1302.dayofweek = 3;		// tue
	ds1302.hours = 11;
	ds1302.minutes = 44;
	ds1302.seconds = 00;
}


// setrtc250331120500
//       YYMMDDHHmmSS
//  date_time에는 241008154500 의 주소
void set_rtc(char *date_time)
{
    char yy[4], mm[4], dd[4];   // date
    char hh[4], min[4], ss[4];  // time

    strncpy(yy, date_time, 2);
    strncpy(mm, date_time+2, 2);
    strncpy(dd, date_time+4, 2);

    strncpy(hh, date_time+6, 2);
    strncpy(min, date_time+8, 2);
    strncpy(ss, date_time+10, 2);

    // 1. ascii --> int --> 2 bcd --> 3 RTC에 적용
    ds1302.year = atoi(yy);
    ds1302.month = atoi(mm);
    ds1302.date = atoi(dd);

    ds1302.hours = atoi(hh);
    ds1302.minutes = atoi(min);
    ds1302.seconds = atoi(ss);

    init_ds1302();
}

 

<flash.c>

#include "main.h"
#include "ds1302.h"
#include <stdio.h>
#include <string.h>

extern t_ds1302 ds1302;
/**************************************************
     Flash module organization(STM32F411)
                               512KBytes

Name        Block base address              size
====      ==================   =======
Sector 0    0x8000000-0x8003FFF           16K bytes
Sector 1    0x8004000-0x8007FFF           16K bytes
Sector 2    0x8008000-0x800BFFF           16K bytes
Sector 3    0x800C000-0x800FFFF           16K bytes
Sector 4    0x8010000-0x801FFFF           64K bytes
Sector 5    0x8020000-0x803FFFF          128K bytes
Sector 6    0x8040000-0x805FFFF          128K bytes
Sector 7    0x8060000-0x807FFFF          128K bytes

******************************************************/

// 0x8060000-0x807FFFF 의 빈공간에 사용자 데이터를 flash programming
// 하도록 설정 한다.
#define ADDR_FLASH_SECTOR7      ((uint32_t) 0x8060000)
#define FLASH_USER_START_ADDR   ((uint32_t) 0x8060000)
#define USER_DATA_ADDRESS        0x8060000
#define FLASH_USER_END_ADDR     ((uint32_t) 0x807FFFF)
#define FLASH_INIT_STATUS       0xFFFFFFFF        // flash 초기 상태
#define FLASH_NOT_INIT_STATUS   0xAAAAAAAA        // flash 초기 상태가 아니다
#define DATA_32                 ((uint32_t) 0x00000001)

HAL_StatusTypeDef flash_read(uint32_t *addr32, int size);
HAL_StatusTypeDef flash_write (uint32_t *data32, int size);
HAL_StatusTypeDef flash_erase();

void flash_main();
void flash_set_time(void);
void set_alarm_time(char *time);

uint32_t flash_read_value=0;

typedef struct student
{
	uint32_t magic;
    int num;        // hakbun
    char name[20];  // name
    double grade;   // hakjum
} t_student;

typedef struct
{
  uint32_t magic;   // 4
  uint8_t Hours;
  uint8_t Minutes;
  uint8_t Seconds;
  uint8_t dummy;    // dummy 1 byte : 메모리 얼라인먼트를 위해서 1 word를 맞추기 위해서
} t_set_time;

t_student student;
t_set_time set_time;

void flash_main()
{
	t_student *read_student;

	flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;

	if (flash_read_value == FLASH_INIT_STATUS)  // 초기에 아무런 데이터도 존재 하지 않을 경우
	{
		flash_erase();
printf("flash EMPTY !!!!\n");
		student.magic=0x55555555;
		student.num=1101815;
		strncpy((char *)&student.name,"Hong_Gil_Dong", strlen("Hong_Gil_Dong"));
		student.grade=4.0;
		printf("w grade: %lf\n", student.grade);
		flash_write((uint32_t *) &student, sizeof(student));
	}
	else   // 1번 이상 flash memory에 데이터를 write 한 경우
	{
		flash_read((uint32_t *)&student, sizeof(student));
		printf("magic: %08x\n", student.magic);
		printf("num: %08x\n", student.num);
		printf("name: %s\n", student.name);
		printf("r grade: %lf\n", student.grade);
	}
}

void set_alarm_time(char *time)
{
	char hh[4], min[4], ss[4];  // time

	strncpy(hh, time, 2);
	strncpy(min,time+2, 2);
	strncpy(ss, time+4, 2);

	// 1. ascii --> int
	set_time.magic=0x55555555;  // 사용자가 임의로 설정
	set_time.Hours = atoi(hh);
	set_time.Minutes = atoi(min);
	set_time.Seconds = atoi(ss);

	flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;

	flash_erase();

	flash_write((uint32_t *) &set_time, sizeof(set_time));
}
void flash_set_time(void)
{
#if 1
	       t_ds1302 *read_set_time;

			flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;

			if (flash_read_value == FLASH_INIT_STATUS)  // 초기에 아무런 데이터도 존재 하지 않을 경우
			{
				flash_erase();
				printf("flash EMPTY !!!!\n");
				init_date_time();
				flash_write((uint32_t *) &ds1302, sizeof(ds1302));
			}
			else   // 1번 이상 flash memory에 데이터를 write 한 경우
			{
				flash_read((uint32_t *)&ds1302, sizeof(ds1302));

				init_ds1302();

				printf("magic: %08x\n", ds1302.magic);
				printf("year: %02d\n", ds1302.year);
				printf("month: %02d\n", ds1302.month);
				printf("date: %02d\n", ds1302.date);
				printf("Hours: %02d\n", ds1302.hours);
				printf("Minutes: %02d\n", ds1302.minutes);
				printf("Seconds: %02d\n", ds1302.seconds);
			}
#else
	t_set_time *read_set_time;

		flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;

		if (flash_read_value == FLASH_INIT_STATUS)  // 초기에 아무런 데이터도 존재 하지 않을 경우
		{
			flash_erase();
	printf("flash EMPTY !!!!\n");
			set_time.magic=0x55555555;  // 사용자가 임의로 설정
			set_time.Hours=11;
			set_time.Minutes=30;
			set_time.Seconds=00;
			flash_write((uint32_t *) &set_time, sizeof(set_time));
		}
		else   // 1번 이상 flash memory에 데이터를 write 한 경우
		{
			flash_read((uint32_t *)&set_time, sizeof(set_time));

			printf("magic: %08x\n", set_time.magic);
			printf("Hours: %02d\n", set_time.Hours);
			printf("Minutes: %02d\n", set_time.Minutes);
			printf("Seconds: %02d\n", set_time.Seconds);
		}
#endif
}
HAL_StatusTypeDef flash_write (uint32_t *data32, int size)
{
	uint32_t *mem32 = data32;

	// mem32 = data32;

  /* Unlock to control */
  HAL_FLASH_Unlock();

  uint32_t Address = FLASH_USER_START_ADDR;

  printf("size: %d\n", size);

  /* Writing data to flash memory */
  for (int i=0; i < size; )
  {
	  if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, *mem32) == HAL_OK)
	  {
		  printf("mem32: %0x\n", *mem32);
		  mem32++;
		  Address = Address + 4;
		  i += 4;
	  }
	  else
	  {
		  uint32_t errorcode = HAL_FLASH_GetError();
		  return HAL_ERROR;
	  }
  }
  /* Lock flash control register */
  HAL_FLASH_Lock();

  return HAL_OK;
}

HAL_StatusTypeDef flash_read(uint32_t *addr32, int size)
{
  uint32_t *data32 = addr32;
  uint32_t address = FLASH_USER_START_ADDR;
  uint32_t end_address = FLASH_USER_START_ADDR + size;

  while(address < end_address)
  {
    *data32 = *(uint32_t*) address;
    data32++;
    address = address + 4;
  }

  return HAL_OK;

}


HAL_StatusTypeDef flash_erase()
{
	uint32_t SectorError = 0;

	/* Unlock to control */
	HAL_FLASH_Unlock();

	/* Calculate sector index */
	uint32_t UserSector = 7;     // sector 번호
	uint32_t NbOfSectors = 1;    // sector 수

	/* Erase sectors */
	FLASH_EraseInitTypeDef EraseInitStruct;
	EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
	EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
	EraseInitStruct.Sector = UserSector;
	EraseInitStruct.NbSectors = NbOfSectors;

	if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK)
	{
		uint32_t errorcode = HAL_FLASH_GetError();
		return HAL_ERROR;
	}

	/* Lock flash control register */
	HAL_FLASH_Lock();

	return HAL_OK;
}

 

<ds1302.h>

/*
 * DS1302.h
 *
 *  Created on: Mar 28, 2025
 *      Author: user
 */

#ifndef INC_DS1302_H_
#define INC_DS1302_H_
//-------------------------include header
#include "main.h"
//-------------------------define
#define ADDR_SECONDS 0x80	//second write addr define
							// read addr은 write addr에 +1만 하면됨
#define ADDR_MINUTES 		0x82
#define ADDR_HOURS 	 		0x84
#define ADDR_DATE 	 		0x86
#define ADDR_MONTH 	 		0x88
#define ADDR_DAYOFWEEK 		0x8a
#define ADDR_YEAR 	 		0x8c
#define ADDR_WRITEPROTECTED 0x8e

typedef struct ds1302
{
	uint8_t magic;
	uint8_t seconds;		//sec
	uint8_t minutes;
	uint8_t hours;
	uint8_t date;
	uint8_t month;
	uint8_t dayofweek;		//1 : sun 2 : mon
	uint8_t year;
	uint8_t ampm;			//1 : pm 0 : am
	uint8_t hourmode;		//0 : 24 1 : 12
} t_ds1302;
t_ds1302 ds1302;

void ds1302_main(void);
void init_date_time(void);
void init_gpio_ds1302(void);
void init_ds1302(void);
void write_ds1302(uint8_t addr, uint8_t data);
void tx_ds1302(uint8_t value);
void clock_ds1302(void);
void input_dataline_ds1302(void);
void output_dataline_ds1302(void);
void read_time_ds1302(void);
void read_date_ds1302(void);
void set_rtc(char *date_time);
uint8_t read_ds1302(uint8_t addr);
void rx_ds1302(unsigned char* pdata);
uint8_t bcd2dec(uint8_t bcd);
uint8_t dec2bcd(uint8_t dec);

#endif /* INC_DS1302_H_ */

 

<실행 결과>

https://youtu.be/Q0Gj4Umk-1w

 

I2C LCD TIMER

<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 ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;

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;

/* 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);
static void MX_I2C1_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();
  MX_I2C1_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

  // i2c_lcd_main();
  ds1302_main();
  //led_main();
  //dht11_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)
  {

//	  button_led_toggle_test();
//	  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);
    /* 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 = 8;
  RCC_OscInitStruct.PLL.PLLN = 84;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  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 I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

/**
  * @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****/

 

<i2c_lcd.c>

/*
 * i2c_lcd.c
 *
 *  Created on: 2019. 9. 4.
 *      Author: k
 */

#include "main.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdio.h>
#include "i2c_lcd.h"

// 외부에서 선언된 I2C 핸들러 (main.c 에 있을 것)
extern I2C_HandleTypeDef hi2c1;

// 함수 선언
void i2c_lcd_main(void);
void i2c_lcd_init(void);

// I2C 주소 설정 (0x27 은 PCF8574 의 기본 주소임, 왼쪽 쉬프트는 HAL이 8bit 주소를 요구해서)
#define I2C_LCD_ADDRESS (0x27 << 1)

// 테스트용 데이터 (아스키 코드로 '4', '3', '\0')
unsigned char lcd_test[4] = { '4','3', 0 };

// 테스트 메인 함수
void i2c_lcd_main(void)
{
//  while (1) // 무한루프
//  {
//	   // lcd_test 배열을 LCD로 송신 (2바이트만 전송)
//	   // 전송 실패 시 계속 재시도 (에러가 발생하면 HAL_OK가 아님)
//	   while(HAL_I2C_Master_Transmit(&hi2c1, I2C_LCD_ADDRESS,
//			 lcd_test, 2, 100)!=HAL_OK){
//		  // 필요하면 딜레이 추가 가능
//	   }
//	   HAL_Delay(1000); // 1초 대기
//  }

#if 1 // 아래 코드는 주석처리되어 있어서 실제로 실행되진 않음 (테스트용 코드)
	uint8_t value=0;
	i2c_lcd_init(); // LCD 초기화

	while(1)
	{
		move_cursor(0,0); // 0행 0열로 커서 이동
		lcd_string("Hello World!!!"); // 문자열 출력
		move_cursor(1,0); // 1행 0열로 커서 이동
		lcd_data(value + '0'); // 숫자를 아스키코드로 변환 후 출력
		value++; // 값 증가
		if(value>9)value=0; // 0~9 순환
		HAL_Delay(500); // 0.5초 대기
	}
#endif
}

// ========================= LCD 명령어 전송 함수 =========================

void lcd_command(uint8_t command){
	// 고위 nibble (상위 4bit) / 저위 nibble (하위 4bit) 분리
	uint8_t high_nibble, low_nibble;
	uint8_t i2c_buffer[4];

	high_nibble = command & 0xf0;
	low_nibble = (command<<4) & 0xf0;

	// en=1 -> en=0 으로 변화시 falling edge를 만들어야 LCD가 latch 함
	// rs=0 (명령어), rw=0 (쓰기), backlight=1

	i2c_buffer[0] = high_nibble | 0x04 | 0x08; // en=1
	i2c_buffer[1] = high_nibble | 0x00 | 0x08; // en=0
	i2c_buffer[2] = low_nibble  | 0x04 | 0x08; // en=1
	i2c_buffer[3] = low_nibble  | 0x00 | 0x08; // en=0

	// I2C 로 전송
	while(HAL_I2C_Master_Transmit(&hi2c1, I2C_LCD_ADDRESS, i2c_buffer, 4, 100)!=HAL_OK){
		// 필요하면 재시도 딜레이
	}
	return;
}

// ========================= LCD 데이터(문자) 전송 함수 =========================

void lcd_data(uint8_t data){
	uint8_t high_nibble, low_nibble;
	uint8_t i2c_buffer[4];

	high_nibble = data & 0xf0;
	low_nibble = (data<<4) & 0xf0;

	// rs=1 (데이터 모드), rw=0 (쓰기)
	i2c_buffer[0] = high_nibble | 0x05 | 0x08; // en=1
	i2c_buffer[1] = high_nibble | 0x01 | 0x08; // en=0
	i2c_buffer[2] = low_nibble  | 0x05 | 0x08; // en=1
	i2c_buffer[3] = low_nibble  | 0x01 | 0x08; // en=0

	while(HAL_I2C_Master_Transmit(&hi2c1, I2C_LCD_ADDRESS, i2c_buffer, 4, 100)!=HAL_OK){
		// 필요하면 재시도 딜레이
	}
	return;
}

// ========================= LCD 초기화 =========================

void i2c_lcd_init(void){
	lcd_command(0x33); // 초기화 과정 (데이터시트 참고)
	lcd_command(0x32); // 4-bit 모드 설정
	lcd_command(0x28); // Function set: 4-bit, 2-line, 5x8 dots
	lcd_command(DISPLAY_ON); // 화면 ON, 커서 OFF, 블링크 OFF (i2c_lcd.h 에 정의되어야 함)
	lcd_command(0x06); // Entry Mode: Increment cursor
	lcd_command(CLEAR_DISPLAY); // 화면 클리어
	HAL_Delay(2); // LCD는 클리어 후 대기 필요
}

// ========================= 문자열 출력 =========================

void lcd_string(uint8_t *str){
	// 문자열 끝(null 문자)까지 반복
	while(*str) lcd_data(*str++);
}

// ========================= 커서 이동 =========================

void move_cursor(uint8_t row, uint8_t column){
	// 커서 이동 명령어
	// 1st line : 0x80 | column
	// 2nd line : 0x80 | 0x40 | column
	lcd_command(0x80 | row<<6 | column);
	return;
}


<i2c_lcd.h>

/*
 * i2c_lcd.h
 *
 *  Created on: 2019. 9. 4.
 *      Author: k
 */

#ifndef SRC_I2C_LCD_H_   // 헤더파일 중복 포함 방지
#define SRC_I2C_LCD_H_

// --------------------[ LCD I2C 관련 설정 ]--------------------

// PCF8574의 기본 주소 (0x27) <<1 은 HAL_I2C 함수가 8비트 주소를 요구하기 때문
#define I2C_LCD_ADDRESS (0x27<<1)

// 백라이트 ON 설정 (PCF8574의 P3 핀에 연결됨, 보통 LCD 밝기 조절용)
#define BACKLIGHT_ON 0x08

// --------------------[ LCD 명령어 정의 ]--------------------

// LCD 화면 켜기 (화면 ON, 커서 OFF, 커서 깜빡임 OFF)
#define DISPLAY_ON 0x0C

// LCD 화면 끄기
#define DISPLAY_OFF 0x08

// LCD 화면 지우기 (클리어)
// 실행 후 반드시 2ms 이상의 딜레이 필요
#define CLEAR_DISPLAY 0x01

// 커서를 홈 위치 (0,0)으로 복귀
#define RETURN_HOME 0x02

// --------------------[ LCD 함수 선언 ]--------------------

// 명령어 전송 함수
void lcd_command(uint8_t command);

// 데이터(문자) 전송 함수
void lcd_data(uint8_t data);

// LCD 초기화 함수
void i2c_lcd_init(void);

// 문자열 출력 함수
void lcd_string(uint8_t *str);

// 커서 이동 함수 (row : 행, column : 열)
void move_cursor(uint8_t row, uint8_t column);

#endif /* SRC_I2C_LCD_H_ */

 

<ds1302.c>

#include "ds1302.h"
#include "extern.h"
#include "i2c_lcd.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

t_ds1302 ds1302;

void ds1302_main(void)
{
	char line1[17];
	char line2[17];

	i2c_lcd_init();

	// init_date_time();
	flash_set_time();
	init_gpio_ds1302();
	init_ds1302();
	int TIM11_sec_counter=0;
	//non os
	while(1)
	{
		read_time_ds1302();
		read_date_ds1302();

		sprintf(line1, "Date: %4d-%02d-%02d",
				ds1302.year+2000,
				ds1302.month,
				ds1302.date);

		sprintf(line2, "Time: %02d:%02d:%02d",
				ds1302.hours,
				ds1302.minutes,
				ds1302.seconds);

		move_cursor(0, 0);
		lcd_string((uint8_t *)line1);

		move_cursor(1, 0);
		lcd_string((uint8_t *)line2);

		HAL_Delay(1000);

		pc_command_processing();
		if(TIM11_1ms_counter > 1000)
		{
			TIM11_1ms_counter = 0;
			// flash_main();
//			flash_read((uint32_t *)&ds1302, sizeof(ds1302));
//
//			printf("magic: %08x\n", ds1302.magic);
//			printf("year: %02d\n", ds1302.year);
//			printf("month: %02d\n", ds1302.month);
//			printf("date: %02d\n", ds1302.date);
//			printf("Hours: %02d\n", ds1302.hours);
//			printf("Minutes: %02d\n", ds1302.minutes);
//			printf("Seconds: %02d\n", ds1302.seconds);
			//날짜와 시각을 출력
			if(o_prt.p_rtc)
				printf(" %4d-%02d-%2d %2d:%2d:%02d\n",
						ds1302.year+2000,
						ds1302.month,
						ds1302.date,
						ds1302.hours,
						ds1302.minutes,
						ds1302.seconds);
			TIM11_sec_counter++;
			if (TIM11_sec_counter > 10)
			{
				TIM11_sec_counter=0;
				printf("TIM11_sec_counter > 5000\n");
				printf("magic: %08x\n", ds1302.magic);
				printf("year: %02d\n", ds1302.year);
				printf("month: %02d\n", ds1302.month);
				printf("date: %02d\n", ds1302.date);
				printf("Hours: %02d\n", ds1302.hours);
				printf("Minutes: %02d\n", ds1302.minutes);
				printf("Seconds: %02d\n", ds1302.seconds);
				flash_erase();
				flash_write((uint32_t *)&ds1302, sizeof(ds1302));
			}
		}
	}
}

void read_time_ds1302(void)
{
	ds1302.seconds = read_ds1302(ADDR_SECONDS);
	ds1302.minutes = read_ds1302(ADDR_MINUTES);
	ds1302.hours = read_ds1302(ADDR_HOURS);
}

void read_date_ds1302(void)
{
	ds1302.date = read_ds1302(ADDR_DATE);
	ds1302.month = read_ds1302(ADDR_MONTH);
	ds1302.dayofweek = read_ds1302(ADDR_DAYOFWEEK);
	ds1302.year = read_ds1302(ADDR_YEAR);
}

uint8_t read_ds1302(uint8_t addr)
{
	unsigned char data8bits = 0;	// 1bits 씩 넘어온 것을 담을 그릇 (변수)
	// 1. CE high
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	// 2. send addr
	tx_ds1302(addr + 1);			// read addr
	// 3. read data
	rx_ds1302(&data8bits);
	// 4. CE low
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	// 5. return bcd to dec
	return bcd2dec(data8bits);
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
//    7654 3210
//    0010 0101 --> bcd
//      2    5
//  2*10^1   5*10^0
// =====================
//        25

//dec -> bcd
uint8_t dec2bcd(uint8_t dec)
{
	uint8_t high, low;

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

	return (high + low);
}

//bcd -> dec
uint8_t bcd2dec(uint8_t bcd)
{
	uint8_t high, low;

	low = bcd & 0x0f;
	high = (bcd >> 4) * 10;			// 0010 0101 >> 4 = 0000 0100 * 10

	return (high + low);
}

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

void init_ds1302(void)
{
	write_ds1302(ADDR_SECONDS, ds1302.seconds);
	write_ds1302(ADDR_MINUTES, ds1302.minutes);
	write_ds1302(ADDR_HOURS, ds1302.hours);
	write_ds1302(ADDR_DATE, ds1302.date);
	write_ds1302(ADDR_MONTH, ds1302.month);
	write_ds1302(ADDR_DAYOFWEEK, ds1302.dayofweek);
	write_ds1302(ADDR_YEAR, ds1302.year);
}

void write_ds1302(uint8_t addr, uint8_t data)
{
	// 1. CE low --> high
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	// 2. addr 전송
	tx_ds1302(addr);
	// 3. data 전송
	tx_ds1302(dec2bcd(data));
	// 4. CE high --> low
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);

}

void tx_ds1302(uint8_t addr)
{
	output_dataline_ds1302();
	// 예) addr 초를 write 하는
	// 80h Msb  >  Lsb
	//     1000 0000 실제값
	//     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 (addr & (1 << i))
		{
			HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
		}
		else
		{
			HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
		}
		clock_ds1302();
	}
}

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 on
	GPIO_init.Pull = GPIO_NOPULL;
	GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW : 2M  HIGH : 25 ~ 100MHz
	HAL_GPIO_Init(GPIOA, &GPIO_init);
}

void input_dataline_ds1302(void)
{
	GPIO_InitTypeDef GPIO_init = { 0, };
	GPIO_init.Pin = IO_DS1302_Pin;
	GPIO_init.Mode = GPIO_MODE_INPUT;		// input mode on
	GPIO_init.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOA, &GPIO_init);
}

void clock_ds1302(void)
{
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}

void init_gpio_ds1302(void)
{
	// ALL LOW
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}

void init_date_time(void)
{
	ds1302.magic=0x55555555;  // 사용자가 임의로 설정
	ds1302.year = 25;
	ds1302.month = 4;
	ds1302.date = 1;
	ds1302.dayofweek = 3;		// tue
	ds1302.hours = 15;
	ds1302.minutes = 40;
	ds1302.seconds = 00;
}


// setrtc250331120500
//       YYMMDDHHmmSS
//  date_time에는 241008154500 의 주소
void set_rtc(char *date_time)
{
    char yy[4], mm[4], dd[4];   // date
    char hh[4], min[4], ss[4];  // time

    strncpy(yy, date_time, 2);
    strncpy(mm, date_time+2, 2);
    strncpy(dd, date_time+4, 2);

    strncpy(hh, date_time+6, 2);
    strncpy(min, date_time+8, 2);
    strncpy(ss, date_time+10, 2);

    // 1. ascii --> int --> 2 bcd --> 3 RTC에 적용
    ds1302.year = atoi(yy);
    ds1302.month = atoi(mm);
    ds1302.date = atoi(dd);

    ds1302.hours = atoi(hh);
    ds1302.minutes = atoi(min);
    ds1302.seconds = atoi(ss);

    init_ds1302();
}

 

<실행 결과>

https://youtube.com/shorts/IQRaqj7NSdA

 

알람시계 구현

<ds1302.c>

#include "ds1302.h"
#include "extern.h"
#include "i2c_lcd.h"
#include "button.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define ALARM_HOUR  16
#define ALARM_MIN   9
#define ALARM_SEC   0

t_ds1302 ds1302;

typedef enum {
	CLOCK_DISPLAY,
	SET_HOUR,
	SET_MIN,
	SET_SEC,
	ALARM_ACTIVE
} AlarmState;

AlarmState currentstate = CLOCK_DISPLAY;

uint8_t alarmHour = 0;
uint8_t alarmMin = 0;
uint8_t alarmSec = 0;

void ds1302_main(void)
{

	// init_date_time();
	flash_set_time();
	init_gpio_ds1302();
	init_ds1302();
	i2c_lcd_init();

	int TIM11_sec_counter=0;
	//non os
	while(1)
	{
		alarm_state_machine();
		HAL_Delay(100);
		read_time_ds1302();
		read_date_ds1302();
		pc_command_processing();
		if(TIM11_1ms_counter > 1000)
		{
			TIM11_1ms_counter = 0;
			// flash_main();
//			flash_read((uint32_t *)&ds1302, sizeof(ds1302));
//
//			printf("magic: %08x\n", ds1302.magic);
//			printf("year: %02d\n", ds1302.year);
//			printf("month: %02d\n", ds1302.month);
//			printf("date: %02d\n", ds1302.date);
//			printf("Hours: %02d\n", ds1302.hours);
//			printf("Minutes: %02d\n", ds1302.minutes);
//			printf("Seconds: %02d\n", ds1302.seconds);
			//날짜와 시각을 출력
			if(o_prt.p_rtc)
				printf(" %4d-%02d-%2d %2d:%2d:%02d\n",
						ds1302.year+2000,
						ds1302.month,
						ds1302.date,
						ds1302.hours,
						ds1302.minutes,
						ds1302.seconds);
			TIM11_sec_counter++;
			if (TIM11_sec_counter > 10)
			{
				TIM11_sec_counter=0;
				printf("TIM11_sec_counter > 5000\n");
				printf("magic: %08x\n", ds1302.magic);
				printf("year: %02d\n", ds1302.year);
				printf("month: %02d\n", ds1302.month);
				printf("date: %02d\n", ds1302.date);
				printf("Hours: %02d\n", ds1302.hours);
				printf("Minutes: %02d\n", ds1302.minutes);
				printf("Seconds: %02d\n", ds1302.seconds);
				flash_erase();
				flash_write((uint32_t *)&ds1302, sizeof(ds1302));
			}
		}
	}
}

void display_clock(void)
{
	char line1[17];
	char line2[17];

	read_time_ds1302();
	read_date_ds1302();

	sprintf(line1, "Date: %4d-%02d-%02d", ds1302.year+2000, ds1302.month, ds1302.date);
	sprintf(line2, "Time: %02d-%02d-%02d", ds1302.hours, ds1302.minutes, ds1302.seconds);

	move_cursor(0, 0);
	lcd_string((uint8_t *)line1);
	move_cursor(1, 0);
	lcd_string((uint8_t *)line2);
}

void clear_lcd_line(uint8_t line)
{
	move_cursor(line, 0);
	lcd_string((uint8_t *)"                "); // 공백 16
}

void alarm_state_machine(void)
{
	GPIOC->PUPDR |= 0x55;
	char buff[17];

	switch(currentstate)
	{
	case CLOCK_DISPLAY:
		display_clock();

		if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
		{
			alarmHour = 0;
			alarmMin = 0;
			alarmSec = 0;
			currentstate = SET_HOUR;
			HAL_Delay(200);
		}
		if((ds1302.hours == alarmHour) && (ds1302.minutes == alarmMin) && (ds1302.seconds == alarmSec))
		{
			currentstate = ALARM_ACTIVE;
		}
		break;

	case SET_HOUR:
		sprintf(buff, "Set Hour: %02d", alarmHour);
		clear_lcd_line(0);
		move_cursor(0, 0);
		lcd_string((uint8_t *)buff);

		if(get_button(GPIOC, GPIO_PIN_1, BTN1) == BUTTON_PRESS)
		{
			alarmHour = (alarmHour + 1) % 24;
		}
		if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
		{
			currentstate = SET_MIN;
			HAL_Delay(200);
		}
		break;

	case SET_MIN:
		sprintf(buff, "Set Min: %02d", alarmMin);
		clear_lcd_line(0);
		move_cursor(0, 0);
		lcd_string((uint8_t *)buff);

		if(get_button(GPIOC, GPIO_PIN_2, BTN2) == BUTTON_PRESS)
		{
			alarmMin = (alarmMin + 1) % 60;
		}
		if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
		{
			currentstate = SET_SEC;
			HAL_Delay(200);
		}
		break;

	case SET_SEC:
		sprintf(buff, "Set Sec: %02d", alarmSec);
		clear_lcd_line(0);
		move_cursor(0, 0);
		lcd_string((uint8_t *)buff);

		if(get_button(GPIOC, GPIO_PIN_3, BTN3) == BUTTON_PRESS)
		{
			alarmSec = (alarmSec + 1) % 60;
		}
		if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
		{
			currentstate = CLOCK_DISPLAY;
			HAL_Delay(200);
		}
		break;

	case ALARM_ACTIVE:
		{
			char alarmDisplay[17];

			sprintf(alarmDisplay, "ALARM %4d-%02d-%02d", ds1302.year+2000, ds1302.month, ds1302.date);
			move_cursor(0, 0);
			lcd_string((uint8_t *)alarmDisplay);
			HAL_Delay(500);
			clear_lcd_line(0);
			HAL_Delay(500);
		}

		if(ds1302.seconds == (alarmSec + 1))
		{
			currentstate = CLOCK_DISPLAY;
			HAL_Delay(200);
		}
		break;

	default:
		currentstate = CLOCK_DISPLAY;
		break;
	}
}

void read_time_ds1302(void)
{
	ds1302.seconds = read_ds1302(ADDR_SECONDS);
	ds1302.minutes = read_ds1302(ADDR_MINUTES);
	ds1302.hours = read_ds1302(ADDR_HOURS);
}

void read_date_ds1302(void)
{
	ds1302.date = read_ds1302(ADDR_DATE);
	ds1302.month = read_ds1302(ADDR_MONTH);
	ds1302.dayofweek = read_ds1302(ADDR_DAYOFWEEK);
	ds1302.year = read_ds1302(ADDR_YEAR);
}

uint8_t read_ds1302(uint8_t addr)
{
	unsigned char data8bits = 0;	// 1bits 씩 넘어온 것을 담을 그릇 (변수)
	// 1. CE high
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	// 2. send addr
	tx_ds1302(addr + 1);			// read addr
	// 3. read data
	rx_ds1302(&data8bits);
	// 4. CE low
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	// 5. return bcd to dec
	return bcd2dec(data8bits);
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
//    7654 3210
//    0010 0101 --> bcd
//      2    5
//  2*10^1   5*10^0
// =====================
//        25

//dec -> bcd
uint8_t dec2bcd(uint8_t dec)
{
	uint8_t high, low;

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

	return (high + low);
}

//bcd -> dec
uint8_t bcd2dec(uint8_t bcd)
{
	uint8_t high, low;

	low = bcd & 0x0f;
	high = (bcd >> 4) * 10;			// 0010 0101 >> 4 = 0000 0100 * 10

	return (high + low);
}

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

void init_ds1302(void)
{
	write_ds1302(ADDR_SECONDS, ds1302.seconds);
	write_ds1302(ADDR_MINUTES, ds1302.minutes);
	write_ds1302(ADDR_HOURS, ds1302.hours);
	write_ds1302(ADDR_DATE, ds1302.date);
	write_ds1302(ADDR_MONTH, ds1302.month);
	write_ds1302(ADDR_DAYOFWEEK, ds1302.dayofweek);
	write_ds1302(ADDR_YEAR, ds1302.year);
}

void write_ds1302(uint8_t addr, uint8_t data)
{
	// 1. CE low --> high
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	// 2. addr 전송
	tx_ds1302(addr);
	// 3. data 전송
	tx_ds1302(dec2bcd(data));
	// 4. CE high --> low
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);

}

void tx_ds1302(uint8_t addr)
{
	output_dataline_ds1302();
	// 예) addr 초를 write 하는
	// 80h Msb  >  Lsb
	//     1000 0000 실제값
	//     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 (addr & (1 << i))
		{
			HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
		}
		else
		{
			HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
		}
		clock_ds1302();
	}
}

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 on
	GPIO_init.Pull = GPIO_NOPULL;
	GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW : 2M  HIGH : 25 ~ 100MHz
	HAL_GPIO_Init(GPIOA, &GPIO_init);
}

void input_dataline_ds1302(void)
{
	GPIO_InitTypeDef GPIO_init = { 0, };
	GPIO_init.Pin = IO_DS1302_Pin;
	GPIO_init.Mode = GPIO_MODE_INPUT;		// input mode on
	GPIO_init.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOA, &GPIO_init);
}

void clock_ds1302(void)
{
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}

void init_gpio_ds1302(void)
{
	// ALL LOW
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
	HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
	HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}

void init_date_time(void)
{
	ds1302.magic=0x55555555;  // 사용자가 임의로 설정
	ds1302.year = 25;
	ds1302.month = 4;
	ds1302.date = 1;
	ds1302.dayofweek = 3;		// tue
	ds1302.hours = 16;
	ds1302.minutes = 8;
	ds1302.seconds = 00;
}


// setrtc250331120500
//       YYMMDDHHmmSS
//  date_time에는 241008154500 의 주소
void set_rtc(char *date_time)
{
    char yy[4], mm[4], dd[4];   // date
    char hh[4], min[4], ss[4];  // time

    strncpy(yy, date_time, 2);
    strncpy(mm, date_time+2, 2);
    strncpy(dd, date_time+4, 2);

    strncpy(hh, date_time+6, 2);
    strncpy(min, date_time+8, 2);
    strncpy(ss, date_time+10, 2);

    // 1. ascii --> int --> 2 bcd --> 3 RTC에 적용
    ds1302.year = atoi(yy);
    ds1302.month = atoi(mm);
    ds1302.date = atoi(dd);

    ds1302.hours = atoi(hh);
    ds1302.minutes = atoi(min);
    ds1302.seconds = atoi(ss);

    init_ds1302();
}

 

<실행 결과>

https://youtube.com/shorts/2gjZ31-JMFs

 

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

8일차  (0) 2025.04.03
7일차  (0) 2025.04.02
5일차  (0) 2025.03.31
4일차  (0) 2025.03.28
3일차  (0) 2025.03.27