Clion开发STM32之W5500系列(NTP服务封装)

概述

  1. 在w5500基础库中进行封装,获取服务端的时间,来校准本地时间。
  2. 本次使用的方案是通过ntp获取时间+定时器更新保证时间准确。

NTP封装

头文件

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
 ******************************************************************************/
#ifndef F1XX_TEMPLATE_W5500_NTP_H
#define F1XX_TEMPLATE_W5500_NTP_H

#include "socket.h"

#define TIMEZONE0 22
#define TIMEZONE8 39
#define TIME_ZONE TIMEZONE8 /*时区*/
#define SECS_PERDAY 86400UL /*一天多少秒*/
#define EPOCH    1900      /*起始年份1900*/

/**
 * @memberof delay_ms_cb 毫秒延迟回调
 * @memberof ntp_server  ntp服务器ip
 * @memberof ntp_port ntp服务端口,默认为123
 */
struct ntp_conf {
    void (*delay_ms_cb)(uint32_t ms);

    uint8_t ntp_server[4];
    uint16_t ntp_port;
};

/**
 * @memberof year 年
 * @memberof month 月
 * @memberof day  日
 * @memberof hour 小时
 * @memberof min 分
 * @memberof sec 秒
 */
struct net_date_time {
    uint16_t year;
    uint8_t month;
    uint8_t day;
    uint8_t hour;
    uint8_t min;
    uint8_t sec;
};

void ntp_config_set(struct ntp_conf *cnf);
/**
 *
 * @param s
 * @param dst [out] 获取时间
 * @return
 */
bool ntp_date_time_get(SOCKET s, uint32_t wait_ms, struct net_date_time *dst);

/**
 * @brief ntp本地时间更新
 */
void ntp_date_time_update();

/**
 * 获取本地时间(note 需要先执行 ntp_date_time_get)
 * @param dst
 * @return
 */
void net_date_time_loc_get(struct net_date_time *dst);

#endif //F1XX_TEMPLATE_W5500_NTP_H

源文件

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
 ******************************************************************************/
#include "w5500_ntp.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "ntp_module"
#define DBG_LEVEL DBG_LOG

#include "sys_dbg.h"

uint32_t volatile total_sec; /*总秒数*/
static struct ntp_conf *ntp_conf_ptr = NULL;
static uint8_t ntp_request_msg[48] = {0x23};
static uint8_t cache_buf[SIZE_256B];
static struct net_date_time nt_tm;

static void calc_date_time(uint32_t seconds, struct net_date_time *datetime);

void get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx, struct net_date_time *dst);

void ntp_config_set(struct ntp_conf *cnf) {
    // note verify conf param
    ntp_conf_ptr = cnf;
}

struct net_date_time *ntp_date_time_get(SOCKET s, uint32_t wait_ms) {
    uint16_t len = 0;
    if (ntp_conf_ptr == NULL) {
        LOG_E("ntp_config_set not set");
        return NULL;
    }
    if (udp_client_init(s, ntp_conf_ptr->ntp_port)) {
        /*发送请求包*/
        udp_client_send_simple(s,
                               ntp_conf_ptr->ntp_server, ntp_conf_ptr->ntp_port,
                               ntp_request_msg, sizeof(ntp_request_msg));
        /*wait 10ms*/
        for (int i = 0; i < wait_ms / 2; ++i) {
            if ((len = w5500_socket_rx_size_read(s)) > 0) {
                recvfrom_simple(s, cache_buf, len);
                /*解析数据*/
                get_seconds_from_ntp_server(cache_buf, 40, &nt_tm);
                close(s); /*关闭当前socket*/
                return &nt_tm;
            }
            ntp_conf_ptr->delay_ms_cb(2);
        }
    } else {
        LOG_E("udp_client_init err:%d", __LINE__);
    }
    return NULL;
}

void ntp_date_time_update() {
    total_sec += 1;
}

struct net_date_time *net_date_time_loc_get() {
    calc_date_time(total_sec, &nt_tm);
    return &nt_tm;
}

void get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx, struct net_date_time *dst) {
    uint32_t seconds = 0;
    uint8_t i = 0;
    for (i = 0; i < 4; i++) {
        seconds = (seconds << 8) | buf[idx + i];
    }
    switch (TIME_ZONE) {
        case 0:
            seconds -= 12 * 3600;
            break;
        case 1:
            seconds -= 11 * 3600;
            break;
        case 2:
            seconds -= 10 * 3600;
            break;
        case 3:
            seconds -= (9 * 3600 + 30 * 60);
            break;
        case 4:
            seconds -= 9 * 3600;
            break;
        case 5:
        case 6:
            seconds -= 8 * 3600;
            break;
        case 7:
        case 8:
            seconds -= 7 * 3600;
            break;
        case 9:
        case 10:
            seconds -= 6 * 3600;
            break;
        case 11:
        case 12:
        case 13:
            seconds -= 5 * 3600;
            break;
        case 14:
            seconds -= (4 * 3600 + 30 * 60);
            break;
        case 15:
        case 16:
            seconds -= 4 * 3600;
            break;
        case 17:
            seconds -= (3 * 3600 + 30 * 60);
            break;
        case 18:
            seconds -= 3 * 3600;
            break;
        case 19:
            seconds -= 2 * 3600;
            break;
        case 20:
            seconds -= 1 * 3600;
            break;
        case 21:
        case 22:
            break;
        case 23:
        case 24:
        case 25:
            seconds += 1 * 3600;
            break;
        case 26:
        case 27:
            seconds += 2 * 3600;
            break;
        case 28:
        case 29:
            seconds += 3 * 3600;
            break;
        case 30:
            seconds += (3 * 3600 + 30 * 60);
            break;
        case 31:
            seconds += 4 * 3600;
            break;
        case 32:
            seconds += (4 * 3600 + 30 * 60);
            break;
        case 33:
            seconds += 5 * 3600;
            break;
        case 34:
            seconds += (5 * 3600 + 30 * 60);
            break;
        case 35:
            seconds += (5 * 3600 + 45 * 60);
            break;
        case 36:
            seconds += 6 * 3600;
            break;
        case 37:
            seconds += (6 * 3600 + 30 * 60);
            break;
        case 38:
            seconds += 7 * 3600;
            break;
        case 39:
            seconds += 8 * 3600;
            break;
        case 40:
            seconds += 9 * 3600;
            break;
        case 41:
            seconds += (9 * 3600 + 30 * 60);
            break;
        case 42:
            seconds += 10 * 3600;
            break;
        case 43:
            seconds += (10 * 3600 + 30 * 60);
            break;
        case 44:
            seconds += 11 * 3600;
            break;
        case 45:
            seconds += (11 * 3600 + 30 * 60);
            break;
        case 46:
            seconds += 12 * 3600;
            break;
        case 47:
            seconds += (12 * 3600 + 45 * 60);
            break;
        case 48:
            seconds += 13 * 3600;
            break;
        case 49:
            seconds += 14 * 3600;
            break;
    }
    total_sec = seconds;
    calc_date_time(seconds, dst);
}

static void calc_date_time(uint32_t seconds, struct net_date_time *datetime) {
    uint8_t yf = 0;
    uint32_t p_year_total_sec;
    uint32_t r_year_total_sec;
    uint32_t n = 0, d = 0, total_d = 0, rz = 0;
    uint16_t y = 0, r = 0, yr = 0;
    signed long long yd = 0;
    n = seconds;
    total_d = seconds / (SECS_PERDAY);
    d = 0;
    p_year_total_sec = SECS_PERDAY * 365;
    r_year_total_sec = SECS_PERDAY * 366;
    while (n >= p_year_total_sec) {
        if ((EPOCH + r) % 400 == 0 || ((EPOCH + r) % 100 != 0 && (EPOCH + r) % 4 == 0)) {
            n = n - (r_year_total_sec);
            d = d + 366;
        } else {
            n = n - (p_year_total_sec);
            d = d + 365;
        }
        r += 1;
        y += 1;

    }

    y += EPOCH;

    datetime->year = y;

    yd = 0;
    yd = total_d - d;

    yf = 1;
    while (yd >= 28) {

        if (yf == 1 || yf == 3 || yf == 5 || yf == 7 || yf == 8 || yf == 10 || yf == 12) {
            yd -= 31;
            if (yd < 0)break;
            rz += 31;
        }

        if (yf == 2) {
            if (y % 400 == 0 || (y % 100 != 0 && y % 4 == 0)) {
                yd -= 29;
                if (yd < 0)break;
                rz += 29;
            } else {
                yd -= 28;
                if (yd < 0)break;
                rz += 28;
            }
        }
        if (yf == 4 || yf == 6 || yf == 9 || yf == 11) {
            yd -= 30;
            if (yd < 0)break;
            rz += 30;
        }
        yf += 1;

    }

    datetime->month = yf;
    yr = total_d - d - rz;

    yr += 1;

    datetime->day = yr;

    seconds = seconds % SECS_PERDAY;
    datetime->hour = seconds / 3600;
    datetime->min = (seconds % 3600) / 60;
    datetime->sec = (seconds % 3600) % 60;

}

测试

配置文件(基础)

/*******************************************************************************
 *  Copyright (c) [scl]。保留所有权利。
 *     本文仅供个人学习和研究使用,禁止用于商业用途。
 ******************************************************************************/

#include "app_conf.h"
#include "w5500_config.h"

#if APP_CONFIG_W5500
#define DBG_ENABLE
#define DBG_SECTION_NAME "w5500"
#define DBG_LEVEL W5500_DBG_LEVEL

#include "sys_dbg.h"
#include "w5500_dns.h"

#define W5500_CS stm_port_define(B,12)
#define W5500_RST stm_port_define(C,7)
static SPI_HandleTypeDef *w5500_spi = NULL;

static void send_and_rec_bytes(uint8_t *in_dat, uint8_t *out_data, uint16_t len) {
    while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);
    HAL_SPI_TransmitReceive(w5500_spi, in_dat, out_data, len, 1000);
    while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);
}

static void send_only(uint8_t *in_data, uint16_t len) {
    HAL_SPI_Transmit(w5500_spi, in_data, len, 1000);
}

static void W5500_RST_HIGH(void) { stm_pin_high(W5500_RST); }

static void W5500_RST_LOW(void) { stm_pin_low(W5500_RST); }

static void W5500_CS_LOW(void) { stm_pin_low(W5500_CS); }

static void W5500_CS_HIGH(void) { stm_pin_high(W5500_CS); }

static void W5500_Driver_MspInit(void) {
    stm32_pin_mode(W5500_CS, pin_mode_output);  /*CS*/
    stm32_pin_mode(W5500_RST, pin_mode_output); /*RST*/
    stm_pin_low(W5500_RST);
    stm_pin_low(W5500_CS);
    /*初始化SPI外设*/
    /*W5500 支持 SPI 模式 0 及模式 3..MOSI 和 MISO 信号无论是接收或发送,均遵从从最高标志位(MSB)到最低标志位(LSB)的传输序列。*/
    bsp_SpiHandleInit(w5500_spi, SPI_BAUDRATEPRESCALER_2, spi_mode_3);
}

module_w5500_t w5500_conf = {
        .base_conf={
                .socket_num = 4,
                .rx_size={4, 4, 4, 4},
                .tx_size={4, 4, 4, 4},
        },
        .net_conf={
                .ip={192, 168, 199, 12},
                .gw={192, 168, 199, 1},
                .sub={255, 255, 255, 0},
                .dns={114, 114, 114, 114},
//                .dns={192, 168, 199, 194},
                .dns_port = 53,
        },
        .driver={
                .cs_high = W5500_CS_HIGH,
                .cs_low = W5500_CS_LOW,
                .rst_high= W5500_RST_HIGH,
                .rst_low=W5500_RST_LOW,
                .delay = HAL_Delay,
                .send_and_rec_bytes = send_and_rec_bytes,
                .send_only =send_only
        },
        .api = {
                .msp_init=W5500_Driver_MspInit,
        }
};


static void w5500_pre_init(void) {
    /*一般做数据加载,此时系统时钟使用的是内部时钟,如需要使用系统时钟的外设不在此进行初始化*/
    w5500_spi = conv_spi_handle_ptr(handle_get_by_id(spi2_id));
    /*初始化资源*/
    module_w5500_init(&w5500_conf);
    uint32_t uid0 = HAL_GetUIDw0();
    uint32_t uid1 = HAL_GetUIDw1();
    uint32_t uid2 = HAL_GetUIDw2();
    uint8_t mac[6] = {0, uid0 >> 8, uid1, uid1 >> 8, uid2, uid2 >> 8};
    memcpy(w5500_conf.net_conf.mac, mac, sizeof(mac));
}

static void w5500_init(void) {

    w5500_conf.api.msp_init();/*初始化*/
    w5500_conf.net_conf_init();
    uint8_t ip[4];
    w5500_reg_ip_read(ip);
    LOG_D("w5500_reg_ip_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
    w5500_reg_gw_read(ip);
    LOG_D("w5500_reg_gw_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}


static void w5500_after_init(void) {

}

app_init_export(w5500_net_conf, w5500_pre_init, w5500_init, w5500_after_init);
#endif





ntp配置文件(ntp+定时器6)

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
    @brief NTP CONF
 ******************************************************************************/

#include "app_conf.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "net_ntp"
#define DBG_LEVEL DBG_LOG

#include "sys_dbg.h"
#include "socket.h"
#include "w5500_ntp.h"

static TIM_HandleTypeDef *ntp_base_timer = NULL;
static struct ntp_conf conf = {
        .ntp_server={114, 118, 7, 163},
        .ntp_port = 123,
        .delay_ms_cb = HAL_Delay
};
struct net_date_time gb_app_time; /*全局使用的时间*/

static void net_ntp_init() {
    ntp_base_timer = conv_tim_handle_ptr(handle_get_by_id(tim6_id));
    bsp_TimHandleInit(ntp_base_timer, 7199, 9999);/*1s*/
    HAL_TIM_Base_Start_IT(ntp_base_timer);

}

sys_init_export(net_ntp, net_ntp_init);

static void net_ntp_after_init() {


    ntp_config_set(&conf);
    uint8_t try_cnt = 3;
    for (int i = 0; i < try_cnt; ++i) {
        if (ntp_date_time_get(1, 500, &gb_app_time)) {
            HAL_TIM_Base_Start(ntp_base_timer);
            goto exit_ok;
        }
    }
    LOG_W("ntp_date_time_get time out");
    return;
    exit_ok:
    LOG_D("NTP TIME:%d-%02d-%02d %02d:%02d:%02d",
          gb_app_time.year, gb_app_time.month, gb_app_time.day,
          gb_app_time.hour, gb_app_time.min, gb_app_time.sec
    );
}

sys_after_init_export(net_ntp, net_ntp_after_init);

void tim6_PeriodElapsedCallback() {
    ntp_date_time_update();
    net_date_time_loc_get(&gb_app_time);
    LOG_D("NTP TIME:%d-%02d-%02d %02d:%02d:%02d",
          gb_app_time.year, gb_app_time.month, gb_app_time.day,
          gb_app_time.hour, gb_app_time.min, gb_app_time.sec
    );

}

void tim6_it_msp_init(void) {
    HAL_NVIC_SetPriority(TIM6_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(TIM6_IRQn);
}

void tim6_it_msp_de_init(void) {
    HAL_NVIC_DisableIRQ(TIM6_IRQn);
}

void TIM6_IRQHandler(void) {
    HAL_TIM_IRQHandler(ntp_base_timer);
}

结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/42906.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【业务功能篇48】后端接口开发的统一规范

业务背景&#xff1a;日常工作中&#xff0c;我们开发接口时&#xff0c;一般都会涉及到参数校验、异常处理、封装结果返回等处理。而我们项目有时为了快速迭代&#xff0c;在这方面上有所疏忽&#xff0c;后续导致代码维护比较难&#xff0c;不同的开发人员的不同习惯&#xf…

整合spring cloud云服务架构 - 企业分布式微服务云架构构建

1. 介绍 Commonservice-system是一个大型分布式、微服务、面向企业的JavaEE体系快速研发平台&#xff0c;基于模块化、服务化、原子化、热插拔的设计思想&#xff0c;使用成熟领先的无商业限制的主流开源技术构建。采用服务化的组件开发模式&#xff0c;可实现复杂的业务功能。…

玩转ChatGPT:Custom instructions (vol. 1)

一、写在前面 据说GPT-4又被削了&#xff0c;前几天让TA改代码&#xff0c;来来回回好几次才成功。 可以看到之前3小时25条的限制&#xff0c;现在改成了3小时50条&#xff0c;可不可以理解为&#xff1a;以前一个指令能完成的任务&#xff0c;现在得两条指令&#xff1f; 可…

leetcode743. 网络延迟时间 DJ

https://leetcode.cn/problems/network-delay-time/ 有 n 个网络节点&#xff0c;标记为 1 到 n。 给你一个列表 times&#xff0c;表示信号经过 有向 边的传递时间。 times[i] (ui, vi, wi)&#xff0c;其中 ui 是源节点&#xff0c;vi 是目标节点&#xff0c; wi 是一个信…

ip、域名、DNS、CDN概念

1、概念 ip地址 在网络世界里, 一台服务器或者说一台网络设备对应着一个ip地址, 如果我们需要访问指定的网络设备的资源, 那么我们就需要知道这个ip地址, 然后才能去访问它. 这就好像, 我想去朋友家里, 我必须先知道他家的住址, 才能去拜访它. 在互联网世界中, 所有的通信都是…

react+redux异步操作数据

reactredux异步操作数据 redux中操作异步方法&#xff0c;主要是&#xff1a; 1、借助createAsyncThunk()封装异步方法&#xff1b;2、通过extraReducers处理异步方法触发后的具体逻辑&#xff0c;操作派生的state 1、异步操作的slice import { createSlice, createAsyncThunk…

Spring MVC -- 返回数据(静态页面+非静态页面+JSON对象+请求转发与请求重定向)

目录 1. 返回静态页面 2. 返回非静态页面 2.1 ResponseBody 返回页面内容 2.2 RestController ResponseBody Controller 2.3 示例:实现简单计算的功能 3. 返回JSON对象 3.1 实现登录功能&#xff0c;返回 JSON 对象 4. 请求转发(forward)或请求重定向(redirect) 4.1 请…

计讯物联5G千兆网关TG463赋能无人船应用方案,开启自动巡检的智能模式

方案背景 水电站、水库、堤坝等水利工程水下构筑物常年处于水下&#xff0c;并在复杂的水流环境下运行&#xff0c;难免会出现磨蚀、露筋等损伤&#xff0c;而传统的安全监测方式一般是通过潜水员检查上层水柱或通过降低水位进行人工巡查&#xff0c;不仅成本高&#xff0c;效…

STM32(HAL库)驱动AD8232心率传感器

目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 ADC外设配置 2.3 串口外设配置 2.4 GPIO配置 2.5 项目生成 3、KEIL端程序整合 3.1 串口重映射 3.2 ADC数据采集 3.3 主函数代码整合 4 硬件连接 5 效果展示 1、简介 本文通过STM32…

Vue3 Vite electron 开发桌面程序

Electron是一个跨平台的桌面应用程序开发框架&#xff0c;它允许开发人员使用Web技术&#xff08;如HTML、CSS和JavaScript&#xff09;构建桌面应用程序&#xff0c;这些应用程序可以在Windows、macOS和Linux等操作系统上运行。 Electron的核心是Chromium浏览器内核和Node.js…

【C++基础(五)】类和对象(上)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C初阶之路⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 类和对象-上 1. 前言2. 类的引入3. 类的定义4. 类的…

Obsidian同步到Notion

插件介绍 将Obsidian的内容同步到Notion需要使用一个第三方插件"Obsidian shared to Notion"EasyChris/obsidian-to-notion: Share obsidian markdown file to notion and generate notion share link 同步obsdian文件到notion&#xff0c;并生成notion分享链接&am…

[SSM]手写Spring框架

目录 十一、手写Spring框架 第一步&#xff1a;创建模块myspring 第二步&#xff1a;准备好要管理的Bean 第三步&#xff1a;准备myspring.xml配置文件 第四步&#xff1a;核心接口实现 第五步&#xff1a;实例化Bean 第六步&#xff1a;给Bean属性赋值 第七步&#xff…

JVM运行时数据区——方法区、堆、栈的关系

方法区存储加载的字节码文件内的相关信息和运行时常量池&#xff0c;方法区可以看作是独立于Java堆的内存空间&#xff0c;方法区是在JVM启动时创建的&#xff0c;其内存的大小可以调整&#xff0c;是线程共享的&#xff0c;并且也会出现内存溢出的情况&#xff0c;也可存在垃圾…

Android - 集成三方模组原厂WiFi Hal库问题

Android - 集成三方模组原厂WiFi Hal库问题 最近Android 11产品平台上需要集成三方WiFi/AP模组厂商提供的hal静态库时遇到一个问题&#xff1a;将三方的库代码集成进系统&#xff0c;并正确配置、编译出lib_driver_cmd_xxx.a(xxx一般是厂商的名字缩写&#xff0c;仅仅是个后缀用…

在英特尔 CPU 上微调 Stable Diffusion 模型

扩散模型能够根据文本提示生成逼真的图像&#xff0c;这种能力促进了生成式人工智能的普及。人们已经开始把这些模型用在包括数据合成及内容创建在内的多个应用领域。Hugging Face Hub 包含超过 5 千个预训练的文生图 模型。这些模型与 Diffusers 库 结合使用&#xff0c;使得构…

【算法基础:搜索与图论】3.4 求最短路算法(Dijkstrabellman-fordspfaFloyd)

文章目录 求最短路算法总览Dijkstra朴素 Dijkstra 算法&#xff08;⭐原理讲解&#xff01;⭐重要&#xff01;&#xff09;&#xff08;用于稠密图&#xff09;例题&#xff1a;849. Dijkstra求最短路 I代码1——使用邻接表代码2——使用邻接矩阵 补充&#xff1a;稠密图和稀疏…

WPF实战项目十(API篇):引入工作单元UnitOfWork

1、通过github地址&#xff1a;https://github.com/arch/UnitOfWork&#xff0c;下载UnitOfWork的代码&#xff0c;将工作单元部分的代码引用到自己的项目&#xff0c;新增UnitOfWork文件夹。 2、在UnitOfWork文件夹下引用UnitOfWork下的IPagedList.cs、PagedList.cs类&#xf…

iOS--编译连接的过程_2

文章目录 iOS编译&#xff08;一&#xff09;编译器前端 编译器后端执行一次XCode build的流程 IPA包的内容二进制文件的内容iOS Link Map File文件说明1. Link Map File 是什么2. Link Map File 有什么用3. 生成 Link Map File查看Link Map File1&#xff09;路径部分计算机系…

Docker 核心概念深度解析:探索容器、镜像和仓库在Docker生态系统中的重要作用和 应用

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…