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>© 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_ */
<실행 결과>
<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일 때>