호타리 2025. 3. 28. 17:55

2025.03.28

 

오늘의 학습 목표

1. UART 활성화

2. 타이밍 분석 & 데이터시트 보고 코딩하기

3. timer INT / I2C

 

RTC(Real Time Clock) DS1302

 

 

DS1302의 Address

 

DS1302 Read Data

 

DS1302 Read Operation Principle

1. GPIO CE SCK IO를 LOW로 설정

2. CE를 HIGH로 설정

3. COMMAND BIT를 I/O로 WRITE한다.

4. CLK를 1번 UP DOWN을 시킨다.

 

3번과 4번을 8번 반복하게 한다.

 

5. I/O PORT INPUT MODE를 1로 바꾼다.

6. DATA 1BIT를 읽는다.

7. CLK를 UP DOWN 시킨다.

 

6번과 7번을 7번 반복한다.

 

8. CE를 HIGH로 설정

 

DS1302 Write Data

 

DS1302 Write Operation Principle

1. GPIO CE SCK IO를 LOW로 설정

2. CE를 HIGH로 설정

3. COMMAND BIT를 I/O로 WRITE한다.

4. CLK를 1번 UP DOWN을 시킨다.

 

3번과 4번을 8번 반복하게 한다.

 

5. DATA 1BIT를 전송한다.

6. CLK를 UP DOWN을 시킨다.

 

6번과 7번을 8번 반복한다.

 

7. CE를 LOW로 설정

 

Real Time Clock

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

/* 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 */
//  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 = 115200;
  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****/

 

<ds1302.c>

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

#include "ds1302.h"

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

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

		HAL_Delay(1000);
	}
}

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; // 1bit씩 넘어온 것을 담을 그릇(변수)
	// 1. CE high
	HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
	// 2. addr 전송
	tx_ds1302(addr + 1); // read addr
	// 3. 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
//       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))
		{
			// 1의 조건만 set
			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 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);
		}
		else
		{
			HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
		}
		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);
}

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);
}

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)
{
	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.year = 25;
	ds1302.month = 3;
	ds1302.date = 28;
	ds1302.dayofweek  = 6; // Fri
	ds1302.hours = 11;
	ds1302.minutes = 30;
	ds1302.seconds = 00;
}

 

<ds1302.h>

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

#ifndef INC_DS1302_H_
#define INC_DS1302_H_

#include "main.h" // HAL GPIO
#define ADDR_SECONDS 0x80 // 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

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

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

t_ds1302 ds1302;

#endif /* INC_DS1302_H_ */

 

<실행 결과>

https://youtu.be/N0mYdVKUbaI

 

<ds1302.c (HAL 방식을 DMA 방식으로 변환)

#define GPIOA_IDR *(unsigned int *)0x40020010

 

if (HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))

if (GPIOA_IDR & IO_DS1302_Pin)

 

#define GPIOA_ODR *(unsigned int *)0x40020014

 

GPIO_InitTypeDef GPIO_init = {0, };

GPIO_init.Pin = IO_DS1302_Pin;

GPIOA_ODR &= ~(1 << 11);

 

HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);

HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);

GPIOA_ODR &= ~(1 << 10);

GPIOA_ODR |= 1 << 11;

 

HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);

HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);

GPIOA_ODR &= ~(1 << 11);

GPIOA_ODR |= 1 << 11;

 

HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);

HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);

GPIOA_ODR &= ~(1 << 12);

GPIOA_ODR |= 1 << 12;

 

#define GPIOA_MODER *(unsigned int *)0x40020000

 

GPIO_init.Mode = GPIO_MODE_INPUT; // input mode

GPIOA_MODER &= ~(0b11 << 22);

 

#define GPIOA_OTYPER *(unsigned int *)0x40020004

 

GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // ouput mode

GPIOA_MODER |= 0b1 << 22;

GPIOA_MODER &= ~(0b1 << 23);

GPIOA_OTYPER &= ~(0b1 << 11);

 

#define GPIOA_PUPDR *(unsigned int *)0x4002000C

 

GPIO_init.Pull = GPIO_NOPULL;

GPIOA_PUPDR &= ~(0b11 << 22);

 

#define GPIOA_OSPEEDR *(unsigned int *)0x40020008

 

GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW: 2MHz, HIGH: 25~100MHz

GPIOA_OSPEEDR |= 0b11 << 22;

 

<ds1302.h>

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

#ifndef INC_DS1302_H_
#define INC_DS1302_H_

#include "main.h" // HAL GPIO
#define ADDR_SECONDS 0x80 // 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

#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

typedef struct ds1302
{
	uint8_t seconds; // sec
	uint8_t minutes; // min
	uint8_t hours;
	uint8_t date;
	uint8_t month;
	uint8_t dayofweek; // 1: sun, 2: moon
	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 read_time_ds1302(t_ds1302 *ds);
void read_date_ds1302(t_ds1302 *ds);
uint8_t read_ds1302(uint8_t addr);
uint8_t bcd2dec(uint8_t bcd);
uint8_t dec2bcd(uint8_t dec);
void rx_ds1302(unsigned char *pdata);
void init_ds1302(t_ds1302 *ds);
void write_ds1302(uint8_t addr, uint8_t data);
void tx_ds1302(uint8_t value);
void input_dataline_ds1302(void);
void output_dataline_ds1302(void);
void clock_ds1302(void);
void init_gpio_ds1302(void);
void init_date_time(t_ds1302 *ds);

#endif /* INC_DS1302_H_ */

 

<ds1302.c>

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

#include "ds1302.h"

void ds1302_main(void)
{
	t_ds1302 ds;

	init_date_time(&ds);
	init_gpio_ds1302();
	init_ds1302(&ds);

	while(1)
	{
		read_time_ds1302(&ds);
		read_date_ds1302(&ds);
		// 날짜와 시각을 출력
		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;
}

 

오실로스코프 파형 분석

 

<전체 파형>

 

<rising edge일 때>

 

<falling edge일 때>