CUBEMX配置STM32实现FTP文件传输以及使用SNTP获取网络时间并写入RTC
引言FTP代码库的移植Cubemx配置SNTP以及RTCRTC配置方法SNTP配置方法FATFS载入RTC时间
总结
引言
在前三篇文章中自己介绍了如何配置freeRTOS以及如何配置LWIP。使用lwip实现一个httpd服务器,使浏览器可以访问,并且我们利用CGI功能,实现通过网页来控制单片机的一个LED灯的电平翻转。在第四篇文章中,我们在单片机16M的Flash上建立了文件系统。
自己写的另外四篇文章 从零开始Cubemx配置STM32搭载freeRTOS实现多路ADC(一) 从零开始Cubemx配置STM32搭载freeRTOS以及lwip实现tcp网络通信(二) 从零开始使用CubeMX配置STM32使用lwip实现httpd服务器以及使用vscode编辑阅读keil代码(三) CubeMX配置STM32实现FatFS文件系统(五)
在本篇文章中我们使用CUBEMX配置STM32实现FTP文件传输以及使用SNTP获取网络时间并写入RTC
FTP代码库的移植
FTP的代码移植我主要参考的这位大佬的文章 【程序】在STM32单片机上用1700行代码实现基于LwIP 2.1.2协议栈raw API和FatFs文件系统的FTP服务器(20200703版) 移植了其项目中的ftpd.c以及ftpd.h这两个文件,这两个文件中对代码有注释可以好好看一下。
移植库前需要做好的准备: 1、lwip库已经移植好,能够正常建立TCP连接。 2、文件系统已经建立好,因为FTP要使用f_open()等函数对文件进行操作。
使用方法:把ftpd.c以及ftpd.h这两个文件添加到工程中,编译通过。在主函数中调用ftpd_init()就可以启用FTP功能。下面是使用的截图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/55fd01d2885d4726881ef7fe793269f5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA54ix5bCP576K,size_20,color_FFFFFF,t_70,g_se,x_16)
注意事项 在使用FTP的时候一定把lwipopts.h中的MEM_SIZE改大点,不然申请内存的时候会不对,导致程序崩溃,默认只有1600字节,我改到了10240字节。
/*----- Default Value for MEM_SIZE: 1600 ---*/
#define MEM_SIZE 10240
Cubemx配置SNTP以及RTC
RTC配置方法
实时时钟的缩写是RTC(Real_Time Clock)。RTC 是集成电路,通常称为时钟芯片。 实时时钟芯片是日常生活中应用最为广泛的消费类电子产品之一。它为人们提供精确的实时时间,或者为电子系统提供精确的时间基准,目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。有些时钟芯片为了在主电源掉电时,还可以工作,需要外加电池供电。 cubemx配置RTC在这个界面进行配置 要注意的是在STM32中,RTC的时钟尽量采用LSE,因为RTC需要断电后继续工作。 这样就配置好了,生成代码就行,使用的代码如下:
RTC_DateTypeDef GetData; //获取日期结构体
RTC_TimeTypeDef GetTime; //获取时间结构体
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
/* Get the RTC current Date */
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
/* Display date Format : yy/mm/dd */
printf("%02d/%02d/%02d\r\n",2000 + GetData.Year, GetData.Month, GetData.Date);
/* Display time Format : hh:mm:ss */
printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
printf("\r\n");
在使用的时候好像必须要先使用HAL_RTC_GetTime获取时间,再使用HAL_RTC_GetDate获取日期,顺序不能颠倒,具体原因我还没看,知道的可以留言一下。下面我们开始配置SNTP,我们要将SNTP中获取的时间赋值给RTC,这样就不用给RTC赋值初值了。
SNTP配置方法
网络时间协议,英文名称:Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),且可介由加密确认的方式来防止恶毒的协议攻击。NTP的目的是在无序的Internet环境中提供精确和健壮的时间服务。
简单网络时间协议(Simple Network Time Protocol,SNTP),由 NTP 改编而来,主要用来同步因特网中的计算机时钟。
配置SNTP的方法很简单,在这个界面开启就行了,配置不用改。 使用SNTP的代码如下,主要看bsp_sntp_init(void)这个函数,在程序初始化的时候调用bsp_sntp_init(void)这个函数实现sntp的初始化。
/**
******************************************************************************
* File Name : RTC.c
* Description : This file provides code for the configuration
* of the RTC instances.
******************************************************************************
* @attention
*
* © Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "rtc.h"
/* USER CODE BEGIN 0 */
#include "sntp.h"
#include
#include "time.h"
/* USER CODE END 0 */
RTC_HandleTypeDef hrtc;
/* RTC init function */
void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 0x12;
sTime.Minutes = 0x10;
sTime.Seconds = 0x10;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_TUESDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 0x18;
sDate.Year = 0x22;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
}
void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspInit 0 */
/* USER CODE END RTC_MspInit 0 */
/* RTC clock enable */
__HAL_RCC_RTC_ENABLE();
/* USER CODE BEGIN RTC_MspInit 1 */
/* USER CODE END RTC_MspInit 1 */
}
}
void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspDeInit 0 */
/* USER CODE END RTC_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_RTC_DISABLE();
/* USER CODE BEGIN RTC_MspDeInit 1 */
/* USER CODE END RTC_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/*!
* @brief 设置 SNTP 的服务器地址,
* 加入多个 IP 以免某个 IP 获取不了时间
* 执行条件:无
*
* @retval: 无
*/
void set_sntp_server_list(void)
{
uint32_t server_list[SNTP_MAX_SERVERS] = {
0x279148D2, //国家授时中心
0x42041876,
0x5F066CCA,
0x0B6C1978,
0x0B0C5CB6,
0x58066BCB,
0x14731978,
0xC51F70CA,
0x521D70CA,
0x820176CA,
0x510176CA,
};
ip_addr_t sntp_server;
for(int i = 0; i
//设置 SNTP 的获取方式 -> 使用向服务器获取方式
sntp_setoperatingmode(SNTP_OPMODE_POLL);
//SNTP 初始化
sntp_init();
//加入授时中心的IP信息
set_sntp_server_list();
}
uint32_t get_timestamp(void)
{
struct tm stm;
static RTC_DateTypeDef g_Date = {0};
static RTC_TimeTypeDef g_Time = {0};
///获取时间必须在获取日期前
HAL_RTC_GetTime(&hrtc, &g_Time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &g_Date, RTC_FORMAT_BIN);
stm.tm_year = g_Date.Year + 100; //RTC_Year rang 0-99,but tm_year since 1900
stm.tm_mon = g_Date.Month - 1; //RTC_Month rang 1-12,but tm_mon rang 0-11
stm.tm_mday = g_Date.Date; //RTC_Date rang 1-31 and tm_mday rang 1-31
stm.tm_hour = g_Time.Hours; //RTC_Hours rang 0-23 and tm_hour rang 0-23
stm.tm_min = g_Time.Minutes; //RTC_Minutes rang 0-59 and tm_min rang 0-59
stm.tm_sec = g_Time.Seconds;
return (mktime(&stm) - (8 * 60 * 60));///配置时由于东八区增加8小时,现为时间戳,需减去
}
void sntp_set_time(uint32_t sntp_time)
{
if(sntp_time == 0)
{
printf("sntp_set_time: wrong!@@\n");
return;
}
printf("sntp_set_time: c00, enter!\n");
printf("sntp_set_time: c01, get time = %u\n", sntp_time);
#if 1
struct tm *time;
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
sntp_time += (8 * 60 * 60); ///北京时间是东8区需偏移8小时
time = localtime(&sntp_time);
/*
* 设置 RTC 的 时间
*/
sTime.Hours = time->tm_hour;
sTime.Minutes = time->tm_min;
sTime.Seconds = time->tm_sec;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
/*
* 设置 RTC 的 日期
*/
sDate.WeekDay = time->tm_wday;
sDate.Month = (time->tm_mon) + 1;
sDate.Date = time->tm_mday;
sDate.Year = (time->tm_year) + 1900 - 2000;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
printf("sntp_set_time: c02, decode time: 20%d-%02d-%02d %02d:%02d:%02d\n", \
sDate.Year, sDate.Month, sDate.Date, sTime.Hours, sTime.Minutes, sTime.Seconds);
printf("sntp_set_time: c03, test get = %u\n", get_timestamp());
printf("sntp_set_time: c04, set rtc time done\n");
#endif
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
还有重要的一步是把自己写的设置时间的函数注册到sntp库中。方法是在lwipopts.h中加入下面的代码:
/*-----------------------------------------------------------------------------*/
/* Current version of LwIP supported by CubeMx: 2.1.2 -*/
/*-----------------------------------------------------------------------------*/
/* Within 'USER CODE' section, code will be kept by default at each generation */
/* USER CODE BEGIN 0 */
#include "rtc.h"
#define SNTP_SET_SYSTEM_TIME sntp_set_time
/* USER CODE END 0 */
使用截图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/34ee79d3a1344016a536dbd2e5295c9a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA54ix5bCP576K,size_20,color_FFFFFF,t_70,g_se,x_16)
FATFS载入RTC时间
之前配置的文件系统不能在创建文件的时候显示当前时间,很不方便,在这里我们把ntp以及RTC都配置好了,把RTC嵌入到FATFS文件系统中,这样创建文件的时间就是当前时间。在fatfs.c的文件中添加以下代码:
DWORD get_fattime(void)
{
/* USER CODE BEGIN get_fattime */
DWORD retValue = 0;
uint8_t year, month, day, hour, minuite, second;
RTC_DateTypeDef GetData; //获取日期结构体
RTC_TimeTypeDef GetTime; //获取时间结构体
year = 2000 + GetData.Year - 1980;
month = GetData.Month;
day = GetData.Date-1;
hour = GetTime.Hours+16;
//printf("Hours is %d\n",(uint8_t)hour);
minuite = GetTime.Minutes;
second = GetTime.Seconds/2;
//printf("Second is %d\n",(uint8_t)second);
retValue = (DWORD)year |