printf("ho_tari\n");
6일차 본문
2025.04.01
오늘의 학습 목표
1. flash memory handling
2. I2C
3. D FlipFlop → Shift Register
STM32F411RE Flash Programming
<STM32F411RE Memory MAP>
- 총 8개의 sector로 구성
- 플래시 메모리는 쓰기전에 먼저 메모리를 지워야 하는데 지울때 사용하는 단위가 sector단위이다.
- 플래시를 지우는 방법은 한번에 칩 전체를 지우거나 sector 하나를 지정해서 지울 수 있다.
Flash 메모리 읽기
- flash 메모리를 읽는 것은 일반 메모리 RAM을 읽는것과 동일 하게 c언어 포인터를 이용 *(읽고자 하는 주소)
- 예) 0x08000000 번지에 저장된 4 byte를 읽고자 할 때
uint32_t val = (((uint32_t *) 0x08000000);
Flash 메모리 쓰기- 플래시 메모리는 바로 쓸 수 없고 먼저 지운 다음에 쓸 수 있다.- 공장 출하시 flash는 모드 지워진 상태이며 모든 비트가 1로 채워져 있다.- 지우고 나면 flash에는 0xFFFFFFFF로 채워 진다.- 평소 지워지지 않게 lock을 걸어 두고 쓰고자 할때만 UNLOCK하여 사용 - FLASH 쓰기 과정 (1) Unlock : HAL_FLASH_Unlock()(2) Erase : HAL_FLASHEx_Erase() : sector 단위 또는 칩 전체(3) Program : HAL_FLASH_Program() : 쓰는 단위는 1, 2, 4, 8 byte 가능(4) Lock : HAL_FLASH_Lock()
Flash programming 문제(1) Alarm 시각을 flash memory에 설정하여 해당 시각이 되면 알람 부저 & Dotmatrix 구동하기(2) 호텔 객실 관리 프로그램을 Doubly Linked list로 구현 후 저장된 data를 Flash에 저장한 후 Power를 껐다 켜면 Flash의 저장 메모리를 읽어서 조회할 수 있도록 program을 구현한다.
DS1302 TIMER Flash Memory
<main.c>
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2025 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "extern.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim11;
UART_HandleTypeDef huart2;
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
.name = "myTask02",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
.name = "myTask03",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; //uart2 rx byte
volatile int TIM11_1ms_counter=0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM11_Init(void);
static void MX_TIM2_Init(void);
static void MX_I2C1_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__ // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE // Add for printf
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART3 and Loop until the end of transmission */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM11_Init();
MX_TIM2_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, &rx_data, 1);
HAL_TIM_Base_Start_IT(&htim11);
HAL_TIM_Base_Start_IT(&htim2); // for make delay_us
// i2c_lcd_main();
ds1302_main();
//led_main();
//dht11_main();
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of myTask02 */
myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);
/* creation of myTask03 */
myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// button_led_toggle_test();
// HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0Xff, 1);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0Xff, 0);
// HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 84;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 84-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4294967295;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
/**
* @brief TIM11 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM11_Init(void)
{
/* USER CODE BEGIN TIM11_Init 0 */
/* USER CODE END TIM11_Init 0 */
/* USER CODE BEGIN TIM11_Init 1 */
/* USER CODE END TIM11_Init 1 */
htim11.Instance = TIM11;
htim11.Init.Prescaler = 84-1;
htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
htim11.Init.Period = 1000-1;
htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM11_Init 2 */
/* USER CODE END TIM11_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
|CLK_DS1302_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PC0 PC1 PC2 PC3 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : DHT11_Pin LD2_Pin CE_DS1302_Pin IO_DS1302_Pin
CLK_DS1302_Pin */
GPIO_InitStruct.Pin = DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
|CLK_DS1302_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 PB3
PB4 PB5 PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
osDelay(50); //
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
button_led_toggle_test();
osDelay(1);
}
/* USER CODE END StartTask02 */
}
/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END StartTask03 */
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM10 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM10) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM11) {
TIM11_1ms_counter++;
}
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
<ds1302.c>
#include "ds1302.h"
#include "extern.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
t_ds1302 ds1302;
void ds1302_main(void)
{
// init_date_time();
flash_set_time();
init_gpio_ds1302();
init_ds1302();
int TIM11_sec_counter=0;
//non os
while(1)
{
read_time_ds1302();
read_date_ds1302();
pc_command_processing();
if(TIM11_1ms_counter > 1000)
{
TIM11_1ms_counter = 0;
// flash_main();
// flash_read((uint32_t *)&ds1302, sizeof(ds1302));
//
// printf("magic: %08x\n", ds1302.magic);
// printf("year: %02d\n", ds1302.year);
// printf("month: %02d\n", ds1302.month);
// printf("date: %02d\n", ds1302.date);
// printf("Hours: %02d\n", ds1302.hours);
// printf("Minutes: %02d\n", ds1302.minutes);
// printf("Seconds: %02d\n", ds1302.seconds);
//날짜와 시각을 출력
if(o_prt.p_rtc)
printf(" %4d-%02d-%2d %2d:%2d:%02d\n",
ds1302.year+2000,
ds1302.month,
ds1302.date,
ds1302.hours,
ds1302.minutes,
ds1302.seconds);
TIM11_sec_counter++;
if (TIM11_sec_counter > 10)
{
TIM11_sec_counter=0;
printf("TIM11_sec_counter > 5000\n");
printf("magic: %08x\n", ds1302.magic);
printf("year: %02d\n", ds1302.year);
printf("month: %02d\n", ds1302.month);
printf("date: %02d\n", ds1302.date);
printf("Hours: %02d\n", ds1302.hours);
printf("Minutes: %02d\n", ds1302.minutes);
printf("Seconds: %02d\n", ds1302.seconds);
flash_erase();
flash_write((uint32_t *)&ds1302, sizeof(ds1302));
}
}
}
}
void read_time_ds1302(void)
{
ds1302.seconds = read_ds1302(ADDR_SECONDS);
ds1302.minutes = read_ds1302(ADDR_MINUTES);
ds1302.hours = read_ds1302(ADDR_HOURS);
}
void read_date_ds1302(void)
{
ds1302.date = read_ds1302(ADDR_DATE);
ds1302.month = read_ds1302(ADDR_MONTH);
ds1302.dayofweek = read_ds1302(ADDR_DAYOFWEEK);
ds1302.year = read_ds1302(ADDR_YEAR);
}
uint8_t read_ds1302(uint8_t addr)
{
unsigned char data8bits = 0; // 1bits 씩 넘어온 것을 담을 그릇 (변수)
// 1. CE high
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
// 2. send addr
tx_ds1302(addr + 1); // read addr
// 3. read data
rx_ds1302(&data8bits);
// 4. CE low
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 5. return bcd to dec
return bcd2dec(data8bits);
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
// 7654 3210
// 0010 0101 --> bcd
// 2 5
// 2*10^1 5*10^0
// =====================
// 25
//dec -> bcd
uint8_t dec2bcd(uint8_t dec)
{
uint8_t high, low;
high = (dec / 10) << 4;
low = dec % 10;
return (high + low);
}
//bcd -> dec
uint8_t bcd2dec(uint8_t bcd)
{
uint8_t high, low;
low = bcd & 0x0f;
high = (bcd >> 4) * 10; // 0010 0101 >> 4 = 0000 0100 * 10
return (high + low);
}
void rx_ds1302(unsigned char *pdata)
{
// IO 포트를 입력 모드로 전환
input_dataline_ds1302();
uint8_t temp = 0;
// DS1302로부터 들어온 bit 를 LSB부터 bit를 받아서 temp 변수에 저장
for(int i = 0; i < 8 ; i++)
{
if(HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))
{
temp |= 1 << i;
}
if (i != 7)
{
clock_ds1302();
}
}
*pdata = temp;
}
void init_ds1302(void)
{
write_ds1302(ADDR_SECONDS, ds1302.seconds);
write_ds1302(ADDR_MINUTES, ds1302.minutes);
write_ds1302(ADDR_HOURS, ds1302.hours);
write_ds1302(ADDR_DATE, ds1302.date);
write_ds1302(ADDR_MONTH, ds1302.month);
write_ds1302(ADDR_DAYOFWEEK, ds1302.dayofweek);
write_ds1302(ADDR_YEAR, ds1302.year);
}
void write_ds1302(uint8_t addr, uint8_t data)
{
// 1. CE low --> high
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
// 2. addr 전송
tx_ds1302(addr);
// 3. data 전송
tx_ds1302(dec2bcd(data));
// 4. CE high --> low
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
}
void tx_ds1302(uint8_t addr)
{
output_dataline_ds1302();
// 예) addr 초를 write 하는
// 80h Msb > Lsb
// 1000 0000 실제값
// 0000 0001 &
// 0000 0000 -> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 1000 0000 실제값 (B7를 전송)
// 1000 0000 &
// 1000 0000 -> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
for (int i = 0; i < 8 ; i++)
{
if (addr & (1 << i))
{
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
}
else
{
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
}
clock_ds1302();
}
}
void output_dataline_ds1302(void)
{
GPIO_InitTypeDef GPIO_init = { 0, };
GPIO_init.Pin = IO_DS1302_Pin;
GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // output mode on
GPIO_init.Pull = GPIO_NOPULL;
GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW : 2M HIGH : 25 ~ 100MHz
HAL_GPIO_Init(GPIOA, &GPIO_init);
}
void input_dataline_ds1302(void)
{
GPIO_InitTypeDef GPIO_init = { 0, };
GPIO_init.Pin = IO_DS1302_Pin;
GPIO_init.Mode = GPIO_MODE_INPUT; // input mode on
GPIO_init.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_init);
}
void clock_ds1302(void)
{
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}
void init_gpio_ds1302(void)
{
// ALL LOW
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}
void init_date_time(void)
{
ds1302.magic=0x55555555; // 사용자가 임의로 설정
ds1302.year = 25;
ds1302.month = 4;
ds1302.date = 1;
ds1302.dayofweek = 3; // tue
ds1302.hours = 11;
ds1302.minutes = 44;
ds1302.seconds = 00;
}
// setrtc250331120500
// YYMMDDHHmmSS
// date_time에는 241008154500 의 주소
void set_rtc(char *date_time)
{
char yy[4], mm[4], dd[4]; // date
char hh[4], min[4], ss[4]; // time
strncpy(yy, date_time, 2);
strncpy(mm, date_time+2, 2);
strncpy(dd, date_time+4, 2);
strncpy(hh, date_time+6, 2);
strncpy(min, date_time+8, 2);
strncpy(ss, date_time+10, 2);
// 1. ascii --> int --> 2 bcd --> 3 RTC에 적용
ds1302.year = atoi(yy);
ds1302.month = atoi(mm);
ds1302.date = atoi(dd);
ds1302.hours = atoi(hh);
ds1302.minutes = atoi(min);
ds1302.seconds = atoi(ss);
init_ds1302();
}
<flash.c>
#include "main.h"
#include "ds1302.h"
#include <stdio.h>
#include <string.h>
extern t_ds1302 ds1302;
/**************************************************
Flash module organization(STM32F411)
512KBytes
Name Block base address size
==== ================== =======
Sector 0 0x8000000-0x8003FFF 16K bytes
Sector 1 0x8004000-0x8007FFF 16K bytes
Sector 2 0x8008000-0x800BFFF 16K bytes
Sector 3 0x800C000-0x800FFFF 16K bytes
Sector 4 0x8010000-0x801FFFF 64K bytes
Sector 5 0x8020000-0x803FFFF 128K bytes
Sector 6 0x8040000-0x805FFFF 128K bytes
Sector 7 0x8060000-0x807FFFF 128K bytes
******************************************************/
// 0x8060000-0x807FFFF 의 빈공간에 사용자 데이터를 flash programming
// 하도록 설정 한다.
#define ADDR_FLASH_SECTOR7 ((uint32_t) 0x8060000)
#define FLASH_USER_START_ADDR ((uint32_t) 0x8060000)
#define USER_DATA_ADDRESS 0x8060000
#define FLASH_USER_END_ADDR ((uint32_t) 0x807FFFF)
#define FLASH_INIT_STATUS 0xFFFFFFFF // flash 초기 상태
#define FLASH_NOT_INIT_STATUS 0xAAAAAAAA // flash 초기 상태가 아니다
#define DATA_32 ((uint32_t) 0x00000001)
HAL_StatusTypeDef flash_read(uint32_t *addr32, int size);
HAL_StatusTypeDef flash_write (uint32_t *data32, int size);
HAL_StatusTypeDef flash_erase();
void flash_main();
void flash_set_time(void);
void set_alarm_time(char *time);
uint32_t flash_read_value=0;
typedef struct student
{
uint32_t magic;
int num; // hakbun
char name[20]; // name
double grade; // hakjum
} t_student;
typedef struct
{
uint32_t magic; // 4
uint8_t Hours;
uint8_t Minutes;
uint8_t Seconds;
uint8_t dummy; // dummy 1 byte : 메모리 얼라인먼트를 위해서 1 word를 맞추기 위해서
} t_set_time;
t_student student;
t_set_time set_time;
void flash_main()
{
t_student *read_student;
flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;
if (flash_read_value == FLASH_INIT_STATUS) // 초기에 아무런 데이터도 존재 하지 않을 경우
{
flash_erase();
printf("flash EMPTY !!!!\n");
student.magic=0x55555555;
student.num=1101815;
strncpy((char *)&student.name,"Hong_Gil_Dong", strlen("Hong_Gil_Dong"));
student.grade=4.0;
printf("w grade: %lf\n", student.grade);
flash_write((uint32_t *) &student, sizeof(student));
}
else // 1번 이상 flash memory에 데이터를 write 한 경우
{
flash_read((uint32_t *)&student, sizeof(student));
printf("magic: %08x\n", student.magic);
printf("num: %08x\n", student.num);
printf("name: %s\n", student.name);
printf("r grade: %lf\n", student.grade);
}
}
void set_alarm_time(char *time)
{
char hh[4], min[4], ss[4]; // time
strncpy(hh, time, 2);
strncpy(min,time+2, 2);
strncpy(ss, time+4, 2);
// 1. ascii --> int
set_time.magic=0x55555555; // 사용자가 임의로 설정
set_time.Hours = atoi(hh);
set_time.Minutes = atoi(min);
set_time.Seconds = atoi(ss);
flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;
flash_erase();
flash_write((uint32_t *) &set_time, sizeof(set_time));
}
void flash_set_time(void)
{
#if 1
t_ds1302 *read_set_time;
flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;
if (flash_read_value == FLASH_INIT_STATUS) // 초기에 아무런 데이터도 존재 하지 않을 경우
{
flash_erase();
printf("flash EMPTY !!!!\n");
init_date_time();
flash_write((uint32_t *) &ds1302, sizeof(ds1302));
}
else // 1번 이상 flash memory에 데이터를 write 한 경우
{
flash_read((uint32_t *)&ds1302, sizeof(ds1302));
init_ds1302();
printf("magic: %08x\n", ds1302.magic);
printf("year: %02d\n", ds1302.year);
printf("month: %02d\n", ds1302.month);
printf("date: %02d\n", ds1302.date);
printf("Hours: %02d\n", ds1302.hours);
printf("Minutes: %02d\n", ds1302.minutes);
printf("Seconds: %02d\n", ds1302.seconds);
}
#else
t_set_time *read_set_time;
flash_read_value = *(__IO uint32_t *) USER_DATA_ADDRESS;
if (flash_read_value == FLASH_INIT_STATUS) // 초기에 아무런 데이터도 존재 하지 않을 경우
{
flash_erase();
printf("flash EMPTY !!!!\n");
set_time.magic=0x55555555; // 사용자가 임의로 설정
set_time.Hours=11;
set_time.Minutes=30;
set_time.Seconds=00;
flash_write((uint32_t *) &set_time, sizeof(set_time));
}
else // 1번 이상 flash memory에 데이터를 write 한 경우
{
flash_read((uint32_t *)&set_time, sizeof(set_time));
printf("magic: %08x\n", set_time.magic);
printf("Hours: %02d\n", set_time.Hours);
printf("Minutes: %02d\n", set_time.Minutes);
printf("Seconds: %02d\n", set_time.Seconds);
}
#endif
}
HAL_StatusTypeDef flash_write (uint32_t *data32, int size)
{
uint32_t *mem32 = data32;
// mem32 = data32;
/* Unlock to control */
HAL_FLASH_Unlock();
uint32_t Address = FLASH_USER_START_ADDR;
printf("size: %d\n", size);
/* Writing data to flash memory */
for (int i=0; i < size; )
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, *mem32) == HAL_OK)
{
printf("mem32: %0x\n", *mem32);
mem32++;
Address = Address + 4;
i += 4;
}
else
{
uint32_t errorcode = HAL_FLASH_GetError();
return HAL_ERROR;
}
}
/* Lock flash control register */
HAL_FLASH_Lock();
return HAL_OK;
}
HAL_StatusTypeDef flash_read(uint32_t *addr32, int size)
{
uint32_t *data32 = addr32;
uint32_t address = FLASH_USER_START_ADDR;
uint32_t end_address = FLASH_USER_START_ADDR + size;
while(address < end_address)
{
*data32 = *(uint32_t*) address;
data32++;
address = address + 4;
}
return HAL_OK;
}
HAL_StatusTypeDef flash_erase()
{
uint32_t SectorError = 0;
/* Unlock to control */
HAL_FLASH_Unlock();
/* Calculate sector index */
uint32_t UserSector = 7; // sector 번호
uint32_t NbOfSectors = 1; // sector 수
/* Erase sectors */
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = UserSector;
EraseInitStruct.NbSectors = NbOfSectors;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK)
{
uint32_t errorcode = HAL_FLASH_GetError();
return HAL_ERROR;
}
/* Lock flash control register */
HAL_FLASH_Lock();
return HAL_OK;
}
<ds1302.h>
/*
* DS1302.h
*
* Created on: Mar 28, 2025
* Author: user
*/
#ifndef INC_DS1302_H_
#define INC_DS1302_H_
//-------------------------include header
#include "main.h"
//-------------------------define
#define ADDR_SECONDS 0x80 //second write addr define
// read addr은 write addr에 +1만 하면됨
#define ADDR_MINUTES 0x82
#define ADDR_HOURS 0x84
#define ADDR_DATE 0x86
#define ADDR_MONTH 0x88
#define ADDR_DAYOFWEEK 0x8a
#define ADDR_YEAR 0x8c
#define ADDR_WRITEPROTECTED 0x8e
typedef struct ds1302
{
uint8_t magic;
uint8_t seconds; //sec
uint8_t minutes;
uint8_t hours;
uint8_t date;
uint8_t month;
uint8_t dayofweek; //1 : sun 2 : mon
uint8_t year;
uint8_t ampm; //1 : pm 0 : am
uint8_t hourmode; //0 : 24 1 : 12
} t_ds1302;
t_ds1302 ds1302;
void ds1302_main(void);
void init_date_time(void);
void init_gpio_ds1302(void);
void init_ds1302(void);
void write_ds1302(uint8_t addr, uint8_t data);
void tx_ds1302(uint8_t value);
void clock_ds1302(void);
void input_dataline_ds1302(void);
void output_dataline_ds1302(void);
void read_time_ds1302(void);
void read_date_ds1302(void);
void set_rtc(char *date_time);
uint8_t read_ds1302(uint8_t addr);
void rx_ds1302(unsigned char* pdata);
uint8_t bcd2dec(uint8_t bcd);
uint8_t dec2bcd(uint8_t dec);
#endif /* INC_DS1302_H_ */
<실행 결과>
I2C LCD TIMER
<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 ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim11;
UART_HandleTypeDef huart2;
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
.name = "myTask02",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
.name = "myTask03",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityLow,
};
/* USER CODE BEGIN PV */
uint8_t rx_data; //uart2 rx byte
volatile int TIM11_1ms_counter=0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM11_Init(void);
static void MX_TIM2_Init(void);
static void MX_I2C1_Init(void);
void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__ // Add for printf
/* With GCC, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE // Add for printf
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART3 and Loop until the end of transmission */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM11_Init();
MX_TIM2_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, &rx_data, 1);
HAL_TIM_Base_Start_IT(&htim11);
HAL_TIM_Base_Start_IT(&htim2); // for make delay_us
// i2c_lcd_main();
ds1302_main();
//led_main();
//dht11_main();
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of myTask02 */
myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);
/* creation of myTask03 */
myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// button_led_toggle_test();
// HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0Xff, 1);
// HAL_Delay(500);
// HAL_GPIO_WritePin(GPIOB, 0Xff, 0);
// HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 84;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 84-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4294967295;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
/**
* @brief TIM11 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM11_Init(void)
{
/* USER CODE BEGIN TIM11_Init 0 */
/* USER CODE END TIM11_Init 0 */
/* USER CODE BEGIN TIM11_Init 1 */
/* USER CODE END TIM11_Init 1 */
htim11.Instance = TIM11;
htim11.Init.Prescaler = 84-1;
htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
htim11.Init.Period = 1000-1;
htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM11_Init 2 */
/* USER CODE END TIM11_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
|CLK_DS1302_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PC0 PC1 PC2 PC3 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : DHT11_Pin LD2_Pin CE_DS1302_Pin IO_DS1302_Pin
CLK_DS1302_Pin */
GPIO_InitStruct.Pin = DHT11_Pin|LD2_Pin|CE_DS1302_Pin|IO_DS1302_Pin
|CLK_DS1302_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 PB3
PB4 PB5 PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
osDelay(50); //
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
button_led_toggle_test();
osDelay(1);
}
/* USER CODE END StartTask02 */
}
/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the myTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END StartTask03 */
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM10 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM10) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM11) {
TIM11_1ms_counter++;
}
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
<i2c_lcd.c>
/*
* i2c_lcd.c
*
* Created on: 2019. 9. 4.
* Author: k
*/
#include "main.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdio.h>
#include "i2c_lcd.h"
// 외부에서 선언된 I2C 핸들러 (main.c 에 있을 것)
extern I2C_HandleTypeDef hi2c1;
// 함수 선언
void i2c_lcd_main(void);
void i2c_lcd_init(void);
// I2C 주소 설정 (0x27 은 PCF8574 의 기본 주소임, 왼쪽 쉬프트는 HAL이 8bit 주소를 요구해서)
#define I2C_LCD_ADDRESS (0x27 << 1)
// 테스트용 데이터 (아스키 코드로 '4', '3', '\0')
unsigned char lcd_test[4] = { '4','3', 0 };
// 테스트 메인 함수
void i2c_lcd_main(void)
{
// while (1) // 무한루프
// {
// // lcd_test 배열을 LCD로 송신 (2바이트만 전송)
// // 전송 실패 시 계속 재시도 (에러가 발생하면 HAL_OK가 아님)
// while(HAL_I2C_Master_Transmit(&hi2c1, I2C_LCD_ADDRESS,
// lcd_test, 2, 100)!=HAL_OK){
// // 필요하면 딜레이 추가 가능
// }
// HAL_Delay(1000); // 1초 대기
// }
#if 1 // 아래 코드는 주석처리되어 있어서 실제로 실행되진 않음 (테스트용 코드)
uint8_t value=0;
i2c_lcd_init(); // LCD 초기화
while(1)
{
move_cursor(0,0); // 0행 0열로 커서 이동
lcd_string("Hello World!!!"); // 문자열 출력
move_cursor(1,0); // 1행 0열로 커서 이동
lcd_data(value + '0'); // 숫자를 아스키코드로 변환 후 출력
value++; // 값 증가
if(value>9)value=0; // 0~9 순환
HAL_Delay(500); // 0.5초 대기
}
#endif
}
// ========================= LCD 명령어 전송 함수 =========================
void lcd_command(uint8_t command){
// 고위 nibble (상위 4bit) / 저위 nibble (하위 4bit) 분리
uint8_t high_nibble, low_nibble;
uint8_t i2c_buffer[4];
high_nibble = command & 0xf0;
low_nibble = (command<<4) & 0xf0;
// en=1 -> en=0 으로 변화시 falling edge를 만들어야 LCD가 latch 함
// rs=0 (명령어), rw=0 (쓰기), backlight=1
i2c_buffer[0] = high_nibble | 0x04 | 0x08; // en=1
i2c_buffer[1] = high_nibble | 0x00 | 0x08; // en=0
i2c_buffer[2] = low_nibble | 0x04 | 0x08; // en=1
i2c_buffer[3] = low_nibble | 0x00 | 0x08; // en=0
// I2C 로 전송
while(HAL_I2C_Master_Transmit(&hi2c1, I2C_LCD_ADDRESS, i2c_buffer, 4, 100)!=HAL_OK){
// 필요하면 재시도 딜레이
}
return;
}
// ========================= LCD 데이터(문자) 전송 함수 =========================
void lcd_data(uint8_t data){
uint8_t high_nibble, low_nibble;
uint8_t i2c_buffer[4];
high_nibble = data & 0xf0;
low_nibble = (data<<4) & 0xf0;
// rs=1 (데이터 모드), rw=0 (쓰기)
i2c_buffer[0] = high_nibble | 0x05 | 0x08; // en=1
i2c_buffer[1] = high_nibble | 0x01 | 0x08; // en=0
i2c_buffer[2] = low_nibble | 0x05 | 0x08; // en=1
i2c_buffer[3] = low_nibble | 0x01 | 0x08; // en=0
while(HAL_I2C_Master_Transmit(&hi2c1, I2C_LCD_ADDRESS, i2c_buffer, 4, 100)!=HAL_OK){
// 필요하면 재시도 딜레이
}
return;
}
// ========================= LCD 초기화 =========================
void i2c_lcd_init(void){
lcd_command(0x33); // 초기화 과정 (데이터시트 참고)
lcd_command(0x32); // 4-bit 모드 설정
lcd_command(0x28); // Function set: 4-bit, 2-line, 5x8 dots
lcd_command(DISPLAY_ON); // 화면 ON, 커서 OFF, 블링크 OFF (i2c_lcd.h 에 정의되어야 함)
lcd_command(0x06); // Entry Mode: Increment cursor
lcd_command(CLEAR_DISPLAY); // 화면 클리어
HAL_Delay(2); // LCD는 클리어 후 대기 필요
}
// ========================= 문자열 출력 =========================
void lcd_string(uint8_t *str){
// 문자열 끝(null 문자)까지 반복
while(*str) lcd_data(*str++);
}
// ========================= 커서 이동 =========================
void move_cursor(uint8_t row, uint8_t column){
// 커서 이동 명령어
// 1st line : 0x80 | column
// 2nd line : 0x80 | 0x40 | column
lcd_command(0x80 | row<<6 | column);
return;
}
<i2c_lcd.h>
/*
* i2c_lcd.h
*
* Created on: 2019. 9. 4.
* Author: k
*/
#ifndef SRC_I2C_LCD_H_ // 헤더파일 중복 포함 방지
#define SRC_I2C_LCD_H_
// --------------------[ LCD I2C 관련 설정 ]--------------------
// PCF8574의 기본 주소 (0x27) <<1 은 HAL_I2C 함수가 8비트 주소를 요구하기 때문
#define I2C_LCD_ADDRESS (0x27<<1)
// 백라이트 ON 설정 (PCF8574의 P3 핀에 연결됨, 보통 LCD 밝기 조절용)
#define BACKLIGHT_ON 0x08
// --------------------[ LCD 명령어 정의 ]--------------------
// LCD 화면 켜기 (화면 ON, 커서 OFF, 커서 깜빡임 OFF)
#define DISPLAY_ON 0x0C
// LCD 화면 끄기
#define DISPLAY_OFF 0x08
// LCD 화면 지우기 (클리어)
// 실행 후 반드시 2ms 이상의 딜레이 필요
#define CLEAR_DISPLAY 0x01
// 커서를 홈 위치 (0,0)으로 복귀
#define RETURN_HOME 0x02
// --------------------[ LCD 함수 선언 ]--------------------
// 명령어 전송 함수
void lcd_command(uint8_t command);
// 데이터(문자) 전송 함수
void lcd_data(uint8_t data);
// LCD 초기화 함수
void i2c_lcd_init(void);
// 문자열 출력 함수
void lcd_string(uint8_t *str);
// 커서 이동 함수 (row : 행, column : 열)
void move_cursor(uint8_t row, uint8_t column);
#endif /* SRC_I2C_LCD_H_ */
<ds1302.c>
#include "ds1302.h"
#include "extern.h"
#include "i2c_lcd.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
t_ds1302 ds1302;
void ds1302_main(void)
{
char line1[17];
char line2[17];
i2c_lcd_init();
// init_date_time();
flash_set_time();
init_gpio_ds1302();
init_ds1302();
int TIM11_sec_counter=0;
//non os
while(1)
{
read_time_ds1302();
read_date_ds1302();
sprintf(line1, "Date: %4d-%02d-%02d",
ds1302.year+2000,
ds1302.month,
ds1302.date);
sprintf(line2, "Time: %02d:%02d:%02d",
ds1302.hours,
ds1302.minutes,
ds1302.seconds);
move_cursor(0, 0);
lcd_string((uint8_t *)line1);
move_cursor(1, 0);
lcd_string((uint8_t *)line2);
HAL_Delay(1000);
pc_command_processing();
if(TIM11_1ms_counter > 1000)
{
TIM11_1ms_counter = 0;
// flash_main();
// flash_read((uint32_t *)&ds1302, sizeof(ds1302));
//
// printf("magic: %08x\n", ds1302.magic);
// printf("year: %02d\n", ds1302.year);
// printf("month: %02d\n", ds1302.month);
// printf("date: %02d\n", ds1302.date);
// printf("Hours: %02d\n", ds1302.hours);
// printf("Minutes: %02d\n", ds1302.minutes);
// printf("Seconds: %02d\n", ds1302.seconds);
//날짜와 시각을 출력
if(o_prt.p_rtc)
printf(" %4d-%02d-%2d %2d:%2d:%02d\n",
ds1302.year+2000,
ds1302.month,
ds1302.date,
ds1302.hours,
ds1302.minutes,
ds1302.seconds);
TIM11_sec_counter++;
if (TIM11_sec_counter > 10)
{
TIM11_sec_counter=0;
printf("TIM11_sec_counter > 5000\n");
printf("magic: %08x\n", ds1302.magic);
printf("year: %02d\n", ds1302.year);
printf("month: %02d\n", ds1302.month);
printf("date: %02d\n", ds1302.date);
printf("Hours: %02d\n", ds1302.hours);
printf("Minutes: %02d\n", ds1302.minutes);
printf("Seconds: %02d\n", ds1302.seconds);
flash_erase();
flash_write((uint32_t *)&ds1302, sizeof(ds1302));
}
}
}
}
void read_time_ds1302(void)
{
ds1302.seconds = read_ds1302(ADDR_SECONDS);
ds1302.minutes = read_ds1302(ADDR_MINUTES);
ds1302.hours = read_ds1302(ADDR_HOURS);
}
void read_date_ds1302(void)
{
ds1302.date = read_ds1302(ADDR_DATE);
ds1302.month = read_ds1302(ADDR_MONTH);
ds1302.dayofweek = read_ds1302(ADDR_DAYOFWEEK);
ds1302.year = read_ds1302(ADDR_YEAR);
}
uint8_t read_ds1302(uint8_t addr)
{
unsigned char data8bits = 0; // 1bits 씩 넘어온 것을 담을 그릇 (변수)
// 1. CE high
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
// 2. send addr
tx_ds1302(addr + 1); // read addr
// 3. read data
rx_ds1302(&data8bits);
// 4. CE low
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 5. return bcd to dec
return bcd2dec(data8bits);
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
// 7654 3210
// 0010 0101 --> bcd
// 2 5
// 2*10^1 5*10^0
// =====================
// 25
//dec -> bcd
uint8_t dec2bcd(uint8_t dec)
{
uint8_t high, low;
high = (dec / 10) << 4;
low = dec % 10;
return (high + low);
}
//bcd -> dec
uint8_t bcd2dec(uint8_t bcd)
{
uint8_t high, low;
low = bcd & 0x0f;
high = (bcd >> 4) * 10; // 0010 0101 >> 4 = 0000 0100 * 10
return (high + low);
}
void rx_ds1302(unsigned char *pdata)
{
// IO 포트를 입력 모드로 전환
input_dataline_ds1302();
uint8_t temp = 0;
// DS1302로부터 들어온 bit 를 LSB부터 bit를 받아서 temp 변수에 저장
for(int i = 0; i < 8 ; i++)
{
if(HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))
{
temp |= 1 << i;
}
if (i != 7)
{
clock_ds1302();
}
}
*pdata = temp;
}
void init_ds1302(void)
{
write_ds1302(ADDR_SECONDS, ds1302.seconds);
write_ds1302(ADDR_MINUTES, ds1302.minutes);
write_ds1302(ADDR_HOURS, ds1302.hours);
write_ds1302(ADDR_DATE, ds1302.date);
write_ds1302(ADDR_MONTH, ds1302.month);
write_ds1302(ADDR_DAYOFWEEK, ds1302.dayofweek);
write_ds1302(ADDR_YEAR, ds1302.year);
}
void write_ds1302(uint8_t addr, uint8_t data)
{
// 1. CE low --> high
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
// 2. addr 전송
tx_ds1302(addr);
// 3. data 전송
tx_ds1302(dec2bcd(data));
// 4. CE high --> low
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
}
void tx_ds1302(uint8_t addr)
{
output_dataline_ds1302();
// 예) addr 초를 write 하는
// 80h Msb > Lsb
// 1000 0000 실제값
// 0000 0001 &
// 0000 0000 -> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 1000 0000 실제값 (B7를 전송)
// 1000 0000 &
// 1000 0000 -> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
for (int i = 0; i < 8 ; i++)
{
if (addr & (1 << i))
{
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
}
else
{
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
}
clock_ds1302();
}
}
void output_dataline_ds1302(void)
{
GPIO_InitTypeDef GPIO_init = { 0, };
GPIO_init.Pin = IO_DS1302_Pin;
GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // output mode on
GPIO_init.Pull = GPIO_NOPULL;
GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW : 2M HIGH : 25 ~ 100MHz
HAL_GPIO_Init(GPIOA, &GPIO_init);
}
void input_dataline_ds1302(void)
{
GPIO_InitTypeDef GPIO_init = { 0, };
GPIO_init.Pin = IO_DS1302_Pin;
GPIO_init.Mode = GPIO_MODE_INPUT; // input mode on
GPIO_init.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_init);
}
void clock_ds1302(void)
{
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}
void init_gpio_ds1302(void)
{
// ALL LOW
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}
void init_date_time(void)
{
ds1302.magic=0x55555555; // 사용자가 임의로 설정
ds1302.year = 25;
ds1302.month = 4;
ds1302.date = 1;
ds1302.dayofweek = 3; // tue
ds1302.hours = 15;
ds1302.minutes = 40;
ds1302.seconds = 00;
}
// setrtc250331120500
// YYMMDDHHmmSS
// date_time에는 241008154500 의 주소
void set_rtc(char *date_time)
{
char yy[4], mm[4], dd[4]; // date
char hh[4], min[4], ss[4]; // time
strncpy(yy, date_time, 2);
strncpy(mm, date_time+2, 2);
strncpy(dd, date_time+4, 2);
strncpy(hh, date_time+6, 2);
strncpy(min, date_time+8, 2);
strncpy(ss, date_time+10, 2);
// 1. ascii --> int --> 2 bcd --> 3 RTC에 적용
ds1302.year = atoi(yy);
ds1302.month = atoi(mm);
ds1302.date = atoi(dd);
ds1302.hours = atoi(hh);
ds1302.minutes = atoi(min);
ds1302.seconds = atoi(ss);
init_ds1302();
}
<실행 결과>
https://youtube.com/shorts/IQRaqj7NSdA
알람시계 구현
<ds1302.c>
#include "ds1302.h"
#include "extern.h"
#include "i2c_lcd.h"
#include "button.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define ALARM_HOUR 16
#define ALARM_MIN 9
#define ALARM_SEC 0
t_ds1302 ds1302;
typedef enum {
CLOCK_DISPLAY,
SET_HOUR,
SET_MIN,
SET_SEC,
ALARM_ACTIVE
} AlarmState;
AlarmState currentstate = CLOCK_DISPLAY;
uint8_t alarmHour = 0;
uint8_t alarmMin = 0;
uint8_t alarmSec = 0;
void ds1302_main(void)
{
// init_date_time();
flash_set_time();
init_gpio_ds1302();
init_ds1302();
i2c_lcd_init();
int TIM11_sec_counter=0;
//non os
while(1)
{
alarm_state_machine();
HAL_Delay(100);
read_time_ds1302();
read_date_ds1302();
pc_command_processing();
if(TIM11_1ms_counter > 1000)
{
TIM11_1ms_counter = 0;
// flash_main();
// flash_read((uint32_t *)&ds1302, sizeof(ds1302));
//
// printf("magic: %08x\n", ds1302.magic);
// printf("year: %02d\n", ds1302.year);
// printf("month: %02d\n", ds1302.month);
// printf("date: %02d\n", ds1302.date);
// printf("Hours: %02d\n", ds1302.hours);
// printf("Minutes: %02d\n", ds1302.minutes);
// printf("Seconds: %02d\n", ds1302.seconds);
//날짜와 시각을 출력
if(o_prt.p_rtc)
printf(" %4d-%02d-%2d %2d:%2d:%02d\n",
ds1302.year+2000,
ds1302.month,
ds1302.date,
ds1302.hours,
ds1302.minutes,
ds1302.seconds);
TIM11_sec_counter++;
if (TIM11_sec_counter > 10)
{
TIM11_sec_counter=0;
printf("TIM11_sec_counter > 5000\n");
printf("magic: %08x\n", ds1302.magic);
printf("year: %02d\n", ds1302.year);
printf("month: %02d\n", ds1302.month);
printf("date: %02d\n", ds1302.date);
printf("Hours: %02d\n", ds1302.hours);
printf("Minutes: %02d\n", ds1302.minutes);
printf("Seconds: %02d\n", ds1302.seconds);
flash_erase();
flash_write((uint32_t *)&ds1302, sizeof(ds1302));
}
}
}
}
void display_clock(void)
{
char line1[17];
char line2[17];
read_time_ds1302();
read_date_ds1302();
sprintf(line1, "Date: %4d-%02d-%02d", ds1302.year+2000, ds1302.month, ds1302.date);
sprintf(line2, "Time: %02d-%02d-%02d", ds1302.hours, ds1302.minutes, ds1302.seconds);
move_cursor(0, 0);
lcd_string((uint8_t *)line1);
move_cursor(1, 0);
lcd_string((uint8_t *)line2);
}
void clear_lcd_line(uint8_t line)
{
move_cursor(line, 0);
lcd_string((uint8_t *)" "); // 공백 16
}
void alarm_state_machine(void)
{
GPIOC->PUPDR |= 0x55;
char buff[17];
switch(currentstate)
{
case CLOCK_DISPLAY:
display_clock();
if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
{
alarmHour = 0;
alarmMin = 0;
alarmSec = 0;
currentstate = SET_HOUR;
HAL_Delay(200);
}
if((ds1302.hours == alarmHour) && (ds1302.minutes == alarmMin) && (ds1302.seconds == alarmSec))
{
currentstate = ALARM_ACTIVE;
}
break;
case SET_HOUR:
sprintf(buff, "Set Hour: %02d", alarmHour);
clear_lcd_line(0);
move_cursor(0, 0);
lcd_string((uint8_t *)buff);
if(get_button(GPIOC, GPIO_PIN_1, BTN1) == BUTTON_PRESS)
{
alarmHour = (alarmHour + 1) % 24;
}
if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
{
currentstate = SET_MIN;
HAL_Delay(200);
}
break;
case SET_MIN:
sprintf(buff, "Set Min: %02d", alarmMin);
clear_lcd_line(0);
move_cursor(0, 0);
lcd_string((uint8_t *)buff);
if(get_button(GPIOC, GPIO_PIN_2, BTN2) == BUTTON_PRESS)
{
alarmMin = (alarmMin + 1) % 60;
}
if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
{
currentstate = SET_SEC;
HAL_Delay(200);
}
break;
case SET_SEC:
sprintf(buff, "Set Sec: %02d", alarmSec);
clear_lcd_line(0);
move_cursor(0, 0);
lcd_string((uint8_t *)buff);
if(get_button(GPIOC, GPIO_PIN_3, BTN3) == BUTTON_PRESS)
{
alarmSec = (alarmSec + 1) % 60;
}
if(get_button(GPIOC, GPIO_PIN_0, BTN0) == BUTTON_PRESS)
{
currentstate = CLOCK_DISPLAY;
HAL_Delay(200);
}
break;
case ALARM_ACTIVE:
{
char alarmDisplay[17];
sprintf(alarmDisplay, "ALARM %4d-%02d-%02d", ds1302.year+2000, ds1302.month, ds1302.date);
move_cursor(0, 0);
lcd_string((uint8_t *)alarmDisplay);
HAL_Delay(500);
clear_lcd_line(0);
HAL_Delay(500);
}
if(ds1302.seconds == (alarmSec + 1))
{
currentstate = CLOCK_DISPLAY;
HAL_Delay(200);
}
break;
default:
currentstate = CLOCK_DISPLAY;
break;
}
}
void read_time_ds1302(void)
{
ds1302.seconds = read_ds1302(ADDR_SECONDS);
ds1302.minutes = read_ds1302(ADDR_MINUTES);
ds1302.hours = read_ds1302(ADDR_HOURS);
}
void read_date_ds1302(void)
{
ds1302.date = read_ds1302(ADDR_DATE);
ds1302.month = read_ds1302(ADDR_MONTH);
ds1302.dayofweek = read_ds1302(ADDR_DAYOFWEEK);
ds1302.year = read_ds1302(ADDR_YEAR);
}
uint8_t read_ds1302(uint8_t addr)
{
unsigned char data8bits = 0; // 1bits 씩 넘어온 것을 담을 그릇 (변수)
// 1. CE high
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
// 2. send addr
tx_ds1302(addr + 1); // read addr
// 3. read data
rx_ds1302(&data8bits);
// 4. CE low
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 5. return bcd to dec
return bcd2dec(data8bits);
}
// 1. 입력 : bcd
// 예) 25년의 bcd --> dec
// 7654 3210
// 0010 0101 --> bcd
// 2 5
// 2*10^1 5*10^0
// =====================
// 25
//dec -> bcd
uint8_t dec2bcd(uint8_t dec)
{
uint8_t high, low;
high = (dec / 10) << 4;
low = dec % 10;
return (high + low);
}
//bcd -> dec
uint8_t bcd2dec(uint8_t bcd)
{
uint8_t high, low;
low = bcd & 0x0f;
high = (bcd >> 4) * 10; // 0010 0101 >> 4 = 0000 0100 * 10
return (high + low);
}
void rx_ds1302(unsigned char *pdata)
{
// IO 포트를 입력 모드로 전환
input_dataline_ds1302();
uint8_t temp = 0;
// DS1302로부터 들어온 bit 를 LSB부터 bit를 받아서 temp 변수에 저장
for(int i = 0; i < 8 ; i++)
{
if(HAL_GPIO_ReadPin(GPIOA, IO_DS1302_Pin))
{
temp |= 1 << i;
}
if (i != 7)
{
clock_ds1302();
}
}
*pdata = temp;
}
void init_ds1302(void)
{
write_ds1302(ADDR_SECONDS, ds1302.seconds);
write_ds1302(ADDR_MINUTES, ds1302.minutes);
write_ds1302(ADDR_HOURS, ds1302.hours);
write_ds1302(ADDR_DATE, ds1302.date);
write_ds1302(ADDR_MONTH, ds1302.month);
write_ds1302(ADDR_DAYOFWEEK, ds1302.dayofweek);
write_ds1302(ADDR_YEAR, ds1302.year);
}
void write_ds1302(uint8_t addr, uint8_t data)
{
// 1. CE low --> high
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
// 2. addr 전송
tx_ds1302(addr);
// 3. data 전송
tx_ds1302(dec2bcd(data));
// 4. CE high --> low
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
}
void tx_ds1302(uint8_t addr)
{
output_dataline_ds1302();
// 예) addr 초를 write 하는
// 80h Msb > Lsb
// 1000 0000 실제값
// 0000 0001 &
// 0000 0000 -> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
// 1000 0000 실제값 (B7를 전송)
// 1000 0000 &
// 1000 0000 -> HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 1);
for (int i = 0; i < 8 ; i++)
{
if (addr & (1 << i))
{
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 1);
}
else
{
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
}
clock_ds1302();
}
}
void output_dataline_ds1302(void)
{
GPIO_InitTypeDef GPIO_init = { 0, };
GPIO_init.Pin = IO_DS1302_Pin;
GPIO_init.Mode = GPIO_MODE_OUTPUT_PP; // output mode on
GPIO_init.Pull = GPIO_NOPULL;
GPIO_init.Speed = GPIO_SPEED_FREQ_HIGH; // LOW : 2M HIGH : 25 ~ 100MHz
HAL_GPIO_Init(GPIOA, &GPIO_init);
}
void input_dataline_ds1302(void)
{
GPIO_InitTypeDef GPIO_init = { 0, };
GPIO_init.Pin = IO_DS1302_Pin;
GPIO_init.Mode = GPIO_MODE_INPUT; // input mode on
GPIO_init.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_init);
}
void clock_ds1302(void)
{
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 1);
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}
void init_gpio_ds1302(void)
{
// ALL LOW
HAL_GPIO_WritePin(GPIOA, CE_DS1302_Pin, 0);
HAL_GPIO_WritePin(GPIOA, IO_DS1302_Pin, 0);
HAL_GPIO_WritePin(GPIOA, CLK_DS1302_Pin, 0);
}
void init_date_time(void)
{
ds1302.magic=0x55555555; // 사용자가 임의로 설정
ds1302.year = 25;
ds1302.month = 4;
ds1302.date = 1;
ds1302.dayofweek = 3; // tue
ds1302.hours = 16;
ds1302.minutes = 8;
ds1302.seconds = 00;
}
// setrtc250331120500
// YYMMDDHHmmSS
// date_time에는 241008154500 의 주소
void set_rtc(char *date_time)
{
char yy[4], mm[4], dd[4]; // date
char hh[4], min[4], ss[4]; // time
strncpy(yy, date_time, 2);
strncpy(mm, date_time+2, 2);
strncpy(dd, date_time+4, 2);
strncpy(hh, date_time+6, 2);
strncpy(min, date_time+8, 2);
strncpy(ss, date_time+10, 2);
// 1. ascii --> int --> 2 bcd --> 3 RTC에 적용
ds1302.year = atoi(yy);
ds1302.month = atoi(mm);
ds1302.date = atoi(dd);
ds1302.hours = atoi(hh);
ds1302.minutes = atoi(min);
ds1302.seconds = atoi(ss);
init_ds1302();
}
<실행 결과>
https://youtube.com/shorts/2gjZ31-JMFs
'(Telechips) AI 시스템 반도체 SW 개발자 교육 > STM32CubeIDE' 카테고리의 다른 글
8일차 (0) | 2025.04.03 |
---|---|
7일차 (0) | 2025.04.02 |
5일차 (0) | 2025.03.31 |
4일차 (0) | 2025.03.28 |
3일차 (0) | 2025.03.27 |