printf("ho_tari\n");
5일차 본문
2025.03.31
오늘의 학습 목표
1. 과제 review
2. UART RX Int Enqueue / Dequeue
3. Timer INT
포인터
- 다른 변수를 가리키는 변수
- 다른 변수의 주소를 담는 변수

- *p는 p가 가리키는 것을 의미
- something pointed by p is integer type
- 변수 p가 어떤 주소 값을 담고 있는데 그 주소를 따라가면 다른 변수가 있고 그 변수는 타입이 정수다.
- 일반적인 변수 값을 담는 그릇이 아니라 주소 값을 담는 그릇
포인터 활용

- 간접 참조 또는 참조 (Dereferencing, Indirection)
- *p = 5; 이면 num에 간접 접근
- num = 5; 이면 num에 직접 접근
포인터.ppt
0.56MB
UART 통신을 이용하여 DS1302에서의 RTC값 보정
<main.c>
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2025 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "extern.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
.name = "myTask02",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
.name = "myTask03",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; // uart2 rx byte
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__ // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE // Add for printf
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART3 and Loop until the end of transmission */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, &rx_data, 1);
// led_main();
ds1302_main();
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of myTask02 */
myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);
/* creation of myTask03 */
myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0xff, 1);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0xff, 0);
// HAL_Delay(500);
// button_led_toggle_test();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PC0 PC1 PC2 PC3 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : LD2_Pin CE_DS1302_Pin IO_DS1302_Pin CLK_DS1302_Pin */
GPIO_InitStruct.Pin = LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 PB3
PB4 PB5 PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
osDelay(50);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
button_led_toggle_test();
osDelay(1);
}
/* USER CODE END StartTask02 */
}
/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END StartTask03 */
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM10 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM10) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
<uart.h>
/*
* uart.h
*
* Created on: Mar 31, 2025
* Author: microsoft
*/
#ifndef INC_UART_H_
#define INC_UART_H_
#include "main.h"
#define COMMAND_NUMBER 20
#define COMMAND_LENGTH 40
// led_all_on\n
// led_all_off\n
volatile uint8_t rx_buff[COMMAND_NUMBER][COMMAND_LENGTH]; // uart0로부터 들어온 문자를 저장하는 버퍼(변수)
volatile int rear = 0; // input index : HAL_UART_RxCpltCallback에서 집어 넣어주는 index
volatile int front = 0; // output index
#endif /* INC_UART_H_ */
<uart.c>
/*
* uart.c
*
* Created on: Mar 31, 2025
* Author: microsoft
*/
#include <string.h>
#include "uart.h"
#include "extern.h"
void pc_command_processing(void);
/**
* @brief Rx Transfer completed callbacks.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
*
* move from Drivers/STM32F4xx_HAL_driver/Src/stm32f4xx_hal_uart.c to here
* 예) comportmaster로부터 1char를 수신하면 HAL_UART_RxCpltCallback으로 진입
* 9600bps인 경우 HAL_UART_RxCpltCallback을 수행 후 1ms이내에는 빠져나가야한다.
* 115200bps 86us
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
volatile static int i = 0;
if(huart == &huart2)
{
if(rx_data == '\n')
{
rx_buff[rear++][i] = '\0';
rear %= COMMAND_NUMBER; // 0~9
i = 0; // 다음 string을 저장하기 위한 1차원 index값을 0으로
// !!!! rx_buff queue full check하는 logic 추가
}
else
{
rx_buff[rear][i++] = rx_data;
// COMMAND_LENGTH를 check하는 logic 추가
}
HAL_UART_Receive_IT(&huart2, &rx_data, 1); // 주의: 반드시 집어 넣어야 다음 INT가 발생
}
}
void pc_command_processing(void)
{
if (front != rear) // rx_buff에 data가 존재
{
printf("%s\n", rx_buff[front]); // &rx_buff[front][0]
if (strncmp((const char *)rx_buff[front], (const char *)"led_all_on", strlen("led_all_on")) == NULL)
{
printf("find : led_all_on\n");
}
else if(strncmp((const char *)rx_buff[front], (const char *)"setrtc", strlen("setrtc")) == NULL)
{
set_rtc((char *)rx_buff[front] + 6);
}
front++;
front %= COMMAND_NUMBER;
// !!!! QUEUE full check하는 logic이 들어가야 한다. !!!!
}
}
<ds1302.c>
/*
* ds1302.c
*
* Created on: Mar 28, 2025
* Author: microsoft
*/
#include "ds1302.h"
t_ds1302 ds;
void ds1302_main(void)
{
init_date_time(&ds);
init_gpio_ds1302();
init_ds1302(&ds);
while(1)
{
read_time_ds1302(&ds);
read_date_ds1302(&ds);
pc_command_processing();
// 날짜와 시각을 출력
printf(" %4d-%2d-%2d %2d:%2d:%2d\n",
ds.year + 2000,
ds.month,
ds.date,
ds.hours,
ds.minutes,
ds.seconds);
HAL_Delay(1000);
}
}
void read_time_ds1302(t_ds1302 *ds)
{
ds->seconds = read_ds1302(ADDR_SECONDS);
ds->minutes = read_ds1302(ADDR_MINUTES);
ds->hours = read_ds1302(ADDR_HOURS);
}
void read_date_ds1302(t_ds1302 *ds)
{
ds->date = read_ds1302(ADDR_DATE);
ds->month = read_ds1302(ADDR_MONTH);
ds->dayofweek = read_ds1302(ADDR_DAYOFWEEK);
ds->year = read_ds1302(ADDR_YEAR);
}
uint8_t read_ds1302(uint8_t addr)
{
unsigned char data8bits = 0; // 1bit씩 넘어온 것을 담을 그릇(변수)
// 1. CE high
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 10;
// 2. addr 전송
tx_ds1302(addr + 1); // read addr
// 3. data를 읽어 들인다.
rx_ds1302(&data8bits);
// 4. CE low
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 10);
// 5. return (bcd to dec )
return (bcd2dec(data8bits));
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
// 7654 3210
// 0010 0101
// 2 5
// + x10 x1
// ==============
// 25
uint8_t bcd2dec(uint8_t bcd)
{
uint8_t high, low;
low = bcd & 0x0f;
high = (bcd >> 4) * 10; // 00100101 bcd >> 4 ==> 000000100 x 10
return (high + low);
}
// dec --> bcd
// 예) 25
// dec bcd
// 00011001 0010 0101
uint8_t dec2bcd(uint8_t dec)
{
uint8_t high, low;
high = (dec / 10) << 4;
low = dec % 10;
return (high + low);
}
void rx_ds1302(unsigned char *pdata)
{
unsigned char temp = 0;
// IO 포트를 입력 모드로 전환
input_dataline_ds1302();
// DS1302로부터 들어온 bit를 LSB부터 8bit를 받아서 temp변수에 저장
for (int i = 0; i < 8; i++)
{
// 1bit를 읽어 들인다.
//if (HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))
if (GPIOA_IDR & (1 << 11))
{
// 1의 조건만 set
temp |= 1 << i;
}
if (i != 7)
{
clock_ds1302();
}
}
*pdata = temp;
}
void init_ds1302(t_ds1302 *ds)
{
write_ds1302(ADDR_SECONDS, ds->seconds);
write_ds1302(ADDR_MINUTES, ds->minutes);
write_ds1302(ADDR_HOURS, ds->hours);
write_ds1302(ADDR_DATE, ds->date);
write_ds1302(ADDR_MONTH, ds->month);
write_ds1302(ADDR_DAYOFWEEK, ds->dayofweek);
write_ds1302(ADDR_YEAR, ds->year);
}
void write_ds1302(uint8_t addr, uint8_t data)
{
// 1. CE low --> high
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 10;
// 2. addr 전송
tx_ds1302(addr);
// 3. data 전송
tx_ds1302(dec2bcd(data));
// 4. CE high --> low
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 10);
}
void tx_ds1302(uint8_t value)
{
output_dataline_ds1302();
// 예) addr 초를 write하는
// 80h M L
// 1000 0000 실제값 (B0를 전송시의 )
// 0000 0001 &
// 0000 0000 ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 1000 0000 실제값 (B7을 전송시의 )
// 1000 0000 &
// 1000 0000 ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
for (int i = 0; i < 8; i++)
{
if (value & (1 << i))
{
// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 11;
}
else
{
// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 11);
}
clock_ds1302();
}
}
void input_dataline_ds1302(void)
{
// GPIO_InitTypeDef GPIO_init = {0, };
// GPIO_init.Pin = IO_DS1302_Pin;
// GPIO_init.Mode = GPIO_MODE_INPUT; // input mode
// GPIO_init.Pull = GPIO_NOPULL;
// HAL_GPIO_Init(GPIOA, &GPIO_init);
GPIOA_ODR &= ~(1 << 11);
GPIOA_MODER &= ~(0b11 << 22);
GPIOA_PUPDR &= ~(0b11 << 22);
}
void output_dataline_ds1302(void)
{
// GPIO_InitTypeDef GPIO_init = {0, };
// GPIO_init.Pin = IO_DS1302_Pin;
// GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // output mode
// GPIO_init.Pull = GPIO_NOPULL;
// GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW: 2MHz, HIGH: 25~100MHz
// HAL_GPIO_Init(GPIOA, &GPIO_init);
GPIOA_ODR &= ~(1 << 11);
GPIOA_MODER |= 0b1 << 22;
GPIOA_MODER &= ~(0b1 << 23);
GPIOA_OTYPER &= ~(0b1 << 11);
GPIOA_PUPDR &= ~(0b11 << 22);
GPIOA_OSPEEDR |= 0b11 << 22;
}
void clock_ds1302(void)
{
// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 12;
// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 12);
}
void init_gpio_ds1302(void)
{
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 10);
// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 11);
// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 12);
}
void init_date_time(t_ds1302 *ds)
{
ds->year = 25;
ds->month = 3;
ds->date = 28;
ds->dayofweek = 6; // Fri
ds->hours = 11;
ds->minutes = 30;
ds->seconds = 00;
}
void set_rtc(char *date_time) // YYMMDDHHmmSS
{
char buf[3] = {0}; // 두자리 문자열과 null 저장 위해 3바이트 배열
uint8_t year, month, date, hours, minutes, seconds;
// 입력된 date_time 문자열에서 각 날짜/시간 분리해서 정수형 값으로 변환
strncpy(buf, date_time, 2); // strncpy로 date_time의 처음 문자 2개를 buf에 복사
ds.year = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 year에 저장
strncpy(buf, date_time + 2, 2); // date_time + 2 위치에서의 문자 2개 buf에 복사
ds.month = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 month에 저장
strncpy(buf, date_time + 4, 2); // date_time + 4 위치에서의 문자 2개 buf에 복사
ds.date = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 date에 저장
strncpy(buf, date_time + 6, 2); // date_time + 6 위치에서의 문자 2개 buf에 복사
ds.hours = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 hours에 저장
strncpy(buf, date_time + 8, 2); // date_time + 8 위치에서의 문자 2개 buf에 복사
ds.minutes = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 minutes에 저장
strncpy(buf, date_time + 10, 2); // date_time + 10 위치에서의 문자 2개 buf에 복사
ds.seconds = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 seconds에 저장
// 해당 레지스터에 기록하기 위해 write_ds1302 함수 호출
init_ds1302(&ds);
}
<실행 결과>
타이머 / 인터럽트 사용

<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 ---------------------------------------------------------*/
TIM_HandleTypeDef htim11;
UART_HandleTypeDef huart2;
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
.name = "myTask02",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
.name = "myTask03",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; // uart2 rx byte
volatile int TIM11_1ms_counter = 0; // timer11의 1ms 카운터
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM11_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__ // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE // Add for printf
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART3 and Loop until the end of transmission */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM11_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, &rx_data, 1);
HAL_TIM_Base_Start_IT(&htim11);
// led_main();
ds1302_main();
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of myTask02 */
myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);
/* creation of myTask03 */
myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0xff, 1);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0xff, 0);
// HAL_Delay(500);
// button_led_toggle_test();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief TIM11 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM11_Init(void)
{
/* USER CODE BEGIN TIM11_Init 0 */
/* USER CODE END TIM11_Init 0 */
/* USER CODE BEGIN TIM11_Init 1 */
/* USER CODE END TIM11_Init 1 */
htim11.Instance = TIM11;
htim11.Init.Prescaler = 84-1;
htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
htim11.Init.Period = 1000-1;
htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM11_Init 2 */
/* USER CODE END TIM11_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PC0 PC1 PC2 PC3 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : LD2_Pin CE_DS1302_Pin IO_DS1302_Pin CLK_DS1302_Pin */
GPIO_InitStruct.Pin = LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin|CLK_DS1302_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 PB3
PB4 PB5 PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
osDelay(50);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
button_led_toggle_test();
osDelay(1);
}
/* USER CODE END StartTask02 */
}
/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END StartTask03 */
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM10 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM10) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM11) {
TIM11_1ms_counter++;
}
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
<ds1302.c>
/*
* ds1302.c
*
* Created on: Mar 28, 2025
* Author: microsoft
*/
#include "ds1302.h"
#include "extern.h"
t_ds1302 ds;
void ds1302_main(void)
{
init_date_time(&ds);
init_gpio_ds1302();
init_ds1302(&ds);
while(1)
{
read_time_ds1302(&ds);
read_date_ds1302(&ds);
pc_command_processing();
if(TIM11_1ms_counter > 1000)
{
TIM11_1ms_counter = 0;
// 날짜와 시각을 출력
printf(" %4d-%2d-%2d %2d:%2d:%2d\n",
ds.year + 2000,
ds.month,
ds.date,
ds.hours,
ds.minutes,
ds.seconds);
// HAL_Delay(1000);
}
}
}
void read_time_ds1302(t_ds1302 *ds)
{
ds->seconds = read_ds1302(ADDR_SECONDS);
ds->minutes = read_ds1302(ADDR_MINUTES);
ds->hours = read_ds1302(ADDR_HOURS);
}
void read_date_ds1302(t_ds1302 *ds)
{
ds->date = read_ds1302(ADDR_DATE);
ds->month = read_ds1302(ADDR_MONTH);
ds->dayofweek = read_ds1302(ADDR_DAYOFWEEK);
ds->year = read_ds1302(ADDR_YEAR);
}
uint8_t read_ds1302(uint8_t addr)
{
unsigned char data8bits = 0; // 1bit씩 넘어온 것을 담을 그릇(변수)
// 1. CE high
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 10;
// 2. addr 전송
tx_ds1302(addr + 1); // read addr
// 3. data를 읽어 들인다.
rx_ds1302(&data8bits);
// 4. CE low
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 10);
// 5. return (bcd to dec )
return (bcd2dec(data8bits));
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
// 7654 3210
// 0010 0101
// 2 5
// + x10 x1
// ==============
// 25
uint8_t bcd2dec(uint8_t bcd)
{
uint8_t high, low;
low = bcd & 0x0f;
high = (bcd >> 4) * 10; // 00100101 bcd >> 4 ==> 000000100 x 10
return (high + low);
}
// dec --> bcd
// 예) 25
// dec bcd
// 00011001 0010 0101
uint8_t dec2bcd(uint8_t dec)
{
uint8_t high, low;
high = (dec / 10) << 4;
low = dec % 10;
return (high + low);
}
void rx_ds1302(unsigned char *pdata)
{
unsigned char temp = 0;
// IO 포트를 입력 모드로 전환
input_dataline_ds1302();
// DS1302로부터 들어온 bit를 LSB부터 8bit를 받아서 temp변수에 저장
for (int i = 0; i < 8; i++)
{
// 1bit를 읽어 들인다.
//if (HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))
if (GPIOA_IDR & (1 << 11))
{
// 1의 조건만 set
temp |= 1 << i;
}
if (i != 7)
{
clock_ds1302();
}
}
*pdata = temp;
}
void init_ds1302(t_ds1302 *ds)
{
write_ds1302(ADDR_SECONDS, ds->seconds);
write_ds1302(ADDR_MINUTES, ds->minutes);
write_ds1302(ADDR_HOURS, ds->hours);
write_ds1302(ADDR_DATE, ds->date);
write_ds1302(ADDR_MONTH, ds->month);
write_ds1302(ADDR_DAYOFWEEK, ds->dayofweek);
write_ds1302(ADDR_YEAR, ds->year);
}
void write_ds1302(uint8_t addr, uint8_t data)
{
// 1. CE low --> high
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 10;
// 2. addr 전송
tx_ds1302(addr);
// 3. data 전송
tx_ds1302(dec2bcd(data));
// 4. CE high --> low
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 10);
}
void tx_ds1302(uint8_t value)
{
output_dataline_ds1302();
// 예) addr 초를 write하는
// 80h M L
// 1000 0000 실제값 (B0를 전송시의 )
// 0000 0001 &
// 0000 0000 ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 1000 0000 실제값 (B7을 전송시의 )
// 1000 0000 &
// 1000 0000 ==> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
for (int i = 0; i < 8; i++)
{
if (value & (1 << i))
{
// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 11;
}
else
{
// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 11);
}
clock_ds1302();
}
}
void input_dataline_ds1302(void)
{
// GPIO_InitTypeDef GPIO_init = {0, };
// GPIO_init.Pin = IO_DS1302_Pin;
// GPIO_init.Mode = GPIO_MODE_INPUT; // input mode
// GPIO_init.Pull = GPIO_NOPULL;
// HAL_GPIO_Init(GPIOA, &GPIO_init);
GPIOA_ODR &= ~(1 << 11);
GPIOA_MODER &= ~(0b11 << 22);
GPIOA_PUPDR &= ~(0b11 << 22);
}
void output_dataline_ds1302(void)
{
// GPIO_InitTypeDef GPIO_init = {0, };
// GPIO_init.Pin = IO_DS1302_Pin;
// GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // output mode
// GPIO_init.Pull = GPIO_NOPULL;
// GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW: 2MHz, HIGH: 25~100MHz
// HAL_GPIO_Init(GPIOA, &GPIO_init);
GPIOA_ODR &= ~(1 << 11);
GPIOA_MODER |= 0b1 << 22;
GPIOA_MODER &= ~(0b1 << 23);
GPIOA_OTYPER &= ~(0b1 << 11);
GPIOA_PUPDR &= ~(0b11 << 22);
GPIOA_OSPEEDR |= 0b11 << 22;
}
void clock_ds1302(void)
{
// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
GPIOA_ODR |= 1 << 12;
// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 12);
}
void init_gpio_ds1302(void)
{
// HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 10);
// HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 11);
// HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
GPIOA_ODR &= ~(1 << 12);
}
void init_date_time(t_ds1302 *ds)
{
ds->year = 25;
ds->month = 3;
ds->date = 28;
ds->dayofweek = 6; // Fri
ds->hours = 11;
ds->minutes = 30;
ds->seconds = 00;
}
void set_rtc(char *date_time) // setrtcYYMMDDHHmmSS
{
char buf[3] = {0}; // 두자리 문자열과 null 저장 위해 3바이트 배열
uint8_t year, month, date, hours, minutes, seconds;
// 입력된 date_time 문자열에서 각 날짜/시간 분리해서 정수형 값으로 변환
strncpy(buf, date_time, 2); // strncpy로 date_time의 처음 문자 2개를 buf에 복사
ds.year = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 year에 저장
strncpy(buf, date_time + 2, 2); // date_time + 2 위치에서의 문자 2개 buf에 복사
ds.month = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 month에 저장
strncpy(buf, date_time + 4, 2); // date_time + 4 위치에서의 문자 2개 buf에 복사
ds.date = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 date에 저장
strncpy(buf, date_time + 6, 2); // date_time + 6 위치에서의 문자 2개 buf에 복사
ds.hours = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 hours에 저장
strncpy(buf, date_time + 8, 2); // date_time + 8 위치에서의 문자 2개 buf에 복사
ds.minutes = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 minutes에 저장
strncpy(buf, date_time + 10, 2); // date_time + 10 위치에서의 문자 2개 buf에 복사
ds.seconds = (uint8_t)atoi(buf); // atoi로 문자열을 정수로 변환하여 seconds에 저장
// 해당 레지스터에 기록하기 위해 write_ds1302 함수 호출
init_ds1302(&ds);
}
<실행 결과>
DHT11 온도 / 습도 측정
<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 ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim11;
UART_HandleTypeDef huart2;
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
.name = "myTask02",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
.name = "myTask03",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; // uart2 rx byte
volatile int TIM11_1ms_counter = 0; // timer11?�� 1ms 카운?��
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM11_Init(void);
static void MX_TIM2_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__ // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE // Add for printf
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART3 and Loop until the end of transmission */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM11_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, &rx_data, 1);
HAL_TIM_Base_Start_IT(&htim11);
HAL_TIM_Base_Start_IT(&htim2); // for make delay_us
dht11_main();
// led_main();
// ds1302_main();
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of myTask02 */
myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);
/* creation of myTask03 */
myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0xff, 1);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0xff, 0);
// HAL_Delay(500);
// button_led_toggle_test();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 84-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4294967295;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
/**
* @brief TIM11 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM11_Init(void)
{
/* USER CODE BEGIN TIM11_Init 0 */
/* USER CODE END TIM11_Init 0 */
/* USER CODE BEGIN TIM11_Init 1 */
/* USER CODE END TIM11_Init 1 */
htim11.Instance = TIM11;
htim11.Init.Prescaler = 84-1;
htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
htim11.Init.Period = 1000-1;
htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM11_Init 2 */
/* USER CODE END TIM11_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
|CLK_DS1302_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PC0 PC1 PC2 PC3 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : DHT11_Pin LD2_Pin CE_DS1302_Pin IO_DS1302_Pin
CLK_DS1302_Pin */
GPIO_InitStruct.Pin = DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
|CLK_DS1302_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 PB3
PB4 PB5 PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
osDelay(50);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
button_led_toggle_test();
osDelay(1);
}
/* USER CODE END StartTask02 */
}
/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END StartTask03 */
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM10 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM10) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM11) {
TIM11_1ms_counter++;
}
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
<dht11.c>
#include "dht11.h"
#include "extern.h"
#include "timer.h"
// us_count : 마이크로초 단위로 DHT11 신호의 HIGH/LOW 지속시간을 측정하기 위한 변수.
// dht11_state : 센서와의 통신 상태를 나타내며, 정상(OK), 타임아웃(TIMEOUT), 데이터 오류(VALUE_ERROR) 등을 포함.
uint8_t us_count = 0;
enum state_define dht11_state = OK;
void init_dht11(void)
{
// DHT_DDR |= 1 << DHT_PIN_NUM; // 데이터 핀(PG0)을 출력 모드로 설정
// DHT_PORT |= 1 << DHT_PIN_NUM; // 출력을 HIGH로 유지하여 기본 풀업 상태를 만듦
GPIOA_MODER &= ~(0b11 << 0);
GPIOA_MODER |= 0b01 << 0;
GPIOA_ODR |= 1 << 0;
}
void dht11_main(void)
{
// 센서에서 수신한 5바이트 데이터 배열:
// [0]: 습도 정수, [1]: 습도 소수, [2]: 온도 정수, [3]: 온도 소수, [4]: 체크섬
uint8_t data[5] = { 0, };
init_dht11();
while (1)
{
memset(data, 0, sizeof(data)); // 데이터 배열 초기화
dht11_state = OK;
// ===== [Step 1: Start Signal] =====
init_dht11(); // 데이터 라인을 HIGH로 초기화 (풀업 상태)
delay_us(100000); // 안정화를 위해 대기
// DHT_PORT &= ~(1 << DHT_PIN_NUM); // 데이터 라인을 LOW로 내려서 시작 신호 전송
GPIOA_ODR &= ~(1 << 0);
delay_us(20000); // 최소 datasheet 권장 18ms 이상 LOW 유지 (여기서는 20ms)
// DHT_PORT |= 1 << DHT_PIN_NUM; // 다시 HIGH로 전환
// DHT_DDR &= ~(1 << DHT_PIN_NUM); // 핀을 입력 모드로 전환하여 센서 응답 수신
GPIOA_ODR |= 1 << 0;
GPIOA_MODER &= ~(0b11 << (0 * 2));
delay_us(1); // 짧은 지연
// ===== [Step 2: Sensor Response Check Part 1] =====
// 센서가 시작 신호에 대해 LOW 응답을 시작하기 전까지 HIGH 상태가 지속되면 에러 처리.
us_count = 0;
// while ( (DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM ) // 아직도 HIGH인지 check
while (((GPIOA_IDR & (1 << 0)) >> 0))
{
delay_us(2);
us_count += 2;
if (us_count > 50) // 50us 이상 HIGH가 지속되면 타임아웃 발생
{
dht11_state = TIMEOUT;
break;
}
}
// ===== [Step 2: Sensor Response Check Part 2] =====
// 센서가 LOW 상태로 전환되었으면, LOW 유지 기간(약 80us, 여기서는 여유를 두어 100us) 체크
if (dht11_state == OK)
{
us_count = 0;
// while ( !((DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM) )
while ( !((GPIOA_IDR & (1 << 0)) >> 0))
{
delay_us(2);
us_count += 2;
if (us_count > 100) // LOW 신호가 100us 이상 지속되지 않으면 타임아웃
{
dht11_state = TIMEOUT;
break;
}
}
}
// ===== [Step 2: Sensor Response Check Part 3] =====
// 이후 센서가 HIGH 상태로 전환되면, HIGH 유지 시간(약 80us, 여유를 두어 100us) 체크
if (dht11_state == OK)
{
us_count = 0;
// while ( (DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM )
while ( ((GPIOA_IDR & (1 << 0)) >> 0))
{
delay_us(2);
us_count += 2;
if (us_count > 100) // HIGH 신호가 너무 길면 타임아웃 처리
{
dht11_state = TIMEOUT;
break;
}
}
}
// 이 시점에서 dht11_state가 OK이면 시작 신호와 센서 응답이 정상적으로 이루어짐.
// ===== [Step 3: Data Bit Reception] =====
// 센서가 40비트 데이터를 순차적으로 전송합니다.
// 각 비트 전송:
// - 50us LOW 펄스로 시작한 후 HIGH 펄스 길이로 비트 값 결정
// * HIGH 펄스가 약 26~28us이면 '0'
// * HIGH 펄스가 약 70us이면 '1'
// 코드에서는 HIGH 펄스 지속시간이 40us 미만이면 '0', 40us 이상이면 '1'로 판정합니다.
if (dht11_state == OK)
{
for (int i = 0; i < 5; i++) // 5바이트 데이터 수신
{
uint8_t pulse[8] = {0, };
// 한 바이트는 8비트로 구성 (MSB부터 LSB 순으로 읽음)
for (int j = 7; j >= 0; j--)
{
// --- LOW 펄스 체크 (각 비트 시작: 약 50us) ---
us_count = 0;
// while ( !((DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM) )
while ( !((GPIOA_IDR & (1 << 0) >> 0)))
{
delay_us(2);
us_count += 2;
if (us_count > 70) // LOW 펄스가 spec(50us)보다 현저히 길면 에러 처리 (여유를 두어 70us)
{
dht11_state = TIMEOUT;
i = 5; // 바깥 for 문 탈출을 위한 변수 재설정
j = -1;
break;
}
}
// --- HIGH 펄스 체크 ---
// LOW 펄스 종료 후, HIGH 펄스의 길이를 측정하여 비트 '0' 또는 '1'을 결정
if (dht11_state == OK)
{
us_count = 0;
// while ( (DHT_PIN & 1 << DHT_PIN_NUM) >> DHT_PIN_NUM ) // HIGH 일동안 반복
while ( ((GPIOA_IDR & (1 << 0)) >> 0))
{
delay_us(2);
us_count += 2;
if (us_count > 90) // HIGH 펄스가 90us 이상이면 타임아웃 (비트 '1'은 약 70us, 여유 고려)
{
dht11_state = TIMEOUT;
i = 5;
j = -1; // for문 전체 탈출
break;
}
}
}
// HIGH 펄스의 길이에 따라 비트 값을 결정: 40us 미만이면 '0', 이상이면 '1'
if (dht11_state == OK)
{
if (us_count < 40) {
pulse[j] = 0;
}
else {
pulse[j] = 1;
}
}
}
// 한 바이트의 8비트가 정상적으로 읽혔다면 pulse 배열을 하나의 바이트로 변환
if (dht11_state == OK) // pulse를 40개를 정상 처리했으면
{
data[i] = pulse[0] << 0 | pulse[1] << 1 | pulse[2] << 2 | pulse[3] << 3 |
pulse[4] << 4 | pulse[5] << 5 | pulse[6] << 6 | pulse[7] << 7;
}
}
// ===== [Step 4: Checksum 검증] =====
// 전송된 5번째 바이트(data[4])는 체크섬으로, 앞 4바이트의 합과 같아야 합니다.
if (dht11_state == OK)
{
if (data[4] != data[0] + data[1] + data[2] + data[3])
{
dht11_state = VALUE_ERROR;
}
}
delay_us(50); // 데이터 전송 후 약간의 여유 시간 (spec: 50us, 여기서는 60us 여유)
}
// ===== [Step 5: 결과 출력 및 안정화 대기] =====
if (dht11_state == OK)
{
printf("temperature : %d.%d\n", data[2], data[3]);
printf("humidity : %d.%d\n", data[0], data[1]);
}
else
{
printf("error code : %d\n", dht11_state);
}
delay_us(2000000); // 센서 안정화 및 다음 측정을 위한 대기 시간
}
}
<dht11.h>
/*
* dht11.h
*
* Created on: Mar 31, 2025
* Author: microsoft
*/
#ifndef INC_DHT11_H_
#define INC_DHT11_H_
#include "main.h"
#define GPIOA_MODER *(unsigned int *)0x40020000
#define GPIOA_OTYPER *(unsigned int *)0x40020004
#define GPIOA_ODR *(unsigned int *)0x40020014
#define GPIOA_IDR *(unsigned int *)0x40020010
#define GPIOA_PUPDR *(unsigned int *)0x4002000C
#define GPIOA_BSRR *(unsigned int *)0x40020018
#define GPIOA_OSPEEDR *(unsigned int *)0x40020008
enum state_define{
OK,
TIMEOUT,
VALUE_ERROR,
TRANS_ERROR
};
extern enum state_define dht11_state;
// 함수 선언
void init_dht11(void);
void dht11_main(void);
void signal_us_check(int duration, int binary_choice);
int dht11_get_bit(void);
extern uint8_t us_count;
#endif /* INC_DHT11_H_ */
<실행 결과>
https://youtube.com/shorts/4xUFONrn_7s
'(Telechips) AI 시스템 반도체 SW 개발자 교육 > STM32CubeIDE' 카테고리의 다른 글
7일차 (0) | 2025.04.02 |
---|---|
6일차 (0) | 2025.04.01 |
4일차 (0) | 2025.03.28 |
3일차 (0) | 2025.03.27 |
2일차 (0) | 2025.03.26 |