CUBEMX配置STM32实现FTP文件传输以及使用SNTP获取网络时间并写入RTC

CUBEMX配置STM32实现FTP文件传输以及使用SNTP获取网络时间并写入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功能。下面是使用的截图
在这里插入图片描述

注意事项
在使用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
  *
  * <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * 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 <stdio.h>
#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_MAX_SERVERS; i++)
	{
		sntp_server.addr = server_list[i];
		sntp_setserver(i, &sntp_server);  // 国家授时中心
	}
}


void bsp_sntp_init(void)
{
	//设置 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 */

使用截图
在这里插入图片描述

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<<25 | (DWORD)month<<21 | (DWORD)day<<16 | (DWORD)hour<<11 | (DWORD)minuite<<5 | (DWORD)second>>1;
  return retValue;

  /* USER CODE END get_fattime */  
}

在这里插入图片描述
这样创建文件的时候就有当前时间了,现在还遗留一个问题没解决,RTC读出的时间是对的,但是转换成DWORD的时候,年月入都对,但是秒会为0,自己还在看,知道怎么回事的可以评论告诉我一下。

总结

自己在这篇文章中写入了FTP如何移植,以及关于时间的一些操作,SNTP,RTC等,并把RTC时间写入了文件系统中,这样一套物联网框架应该就比较全了,自己下周开始搞一下MQTT。

  • 6
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值