【RL-TCPnet网络教程】第30章 RL-TCPnet之SNTP网络时间获取

时间:2023-03-09 12:50:27
【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

第30章      RL-TCPnet之SNTP网络时间获取

本章节为大家讲解RL-TCPnet的SNTP应用,学习本章节前,务必要优先学习第29章的NTP基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

本章教程含STM32F407开发板和STM32F429开发板。

30.1  初学者重要提示

30.2  可用的NTP服务器

30.3  SNTP函数

30.4  SNTP配置说明(Net_Config.c)

30.5  SNTP调试说明(Net_Debug.c)

30.6  网络调试助手和板子的操作步骤

30.7  实验例程说明(裸机)

30.8  实验例程说明(RTX)

30.9  总结

30.1  初学者重要提示

  1. 学习本章节前,务必保证已经学习了第29章的基础知识。
  2. 本章相对比较简单,测试本章节的例子,务必将其接到能够联网的路由器或者交换机上

30.2  可用的NTP服务器

国内可用的NTP服务器已经在帖子:http://bbs.armfly.com/read.php?tid=31397 里面进行了简单的总结,我们这里使用IP地址为182.16.3.162的NTP服务器。

30.3  SNTP函数

涉及到SNTP的,仅有如下一个函数:

  • sntp_get_time

关于这个函数的讲解及其使用方法可以看教程第 3 章 3.4 小节里面说的参考资料 rlarm.chm 文件:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

注意,这个函数不支持重入,也就是不支持多任务调用。

30.3.1   函数sntp_get_time

函数原型:

BOOL sntp_get_time (

          U8*   ipadr,        /* NTP/SNTP服务器IP地址 */

          void (*cbfunc)(     /* 回调函数,接收到NTP消息会触发 */

           U32 utc_time) );   /* 接收到的UNIX时间戳,从1970.1.1开始所经历的秒数 */

函数描述:

函数sntp_get_time用于从NTP服务器获得UNIX时间戳,这个函数支持单播和广播两种模式。单播方式下,通过此函数给远程NTP服务器发送获取时间消息。广播模式下,将打开UDP Socket接收NTP广播消息,如果局域网内有NTP服务器,可以采用这种模式。(广播和单播模式是在Net_Config.c文件中设置的,如果没有选择广播Broadcast模式,就表示单播,否则表示广播。

  1. 第1个参数是NTP服务器的IP地址。
    • 单播模式,这个参数就是远程NTP服务器的IP地址。
    • 广播模式,这个参数是局域网内NTP服务器的IP地址,如果用户设置了指定的IP地址,那么将仅接收此服务器的消息,其它服务器的消息忽略。如果此IP地址被设置为0.0.0.0,那么将接收局域网内任何NTP服务器的消息。
  2. 第2个参数是回调函数,回调函数有一个参数,这个参数utc_time代表的含义如下:
    • 调用此函数返回的UNIX时间戳,从1970.1.1开始所经历的秒数,如果此数值是0的话(数值0被保留用于表示返回失败),表示获取失败。
  3. 返回值有以下两种,返回__TURE,单播模式下表示SNTP消息发送成功,广播模式下表示UDP Socket打开成功。返回__FALSE,单播模式下表示发送失败,广播模式下表示UDP Socket打开失败。

使用这个函数要注意以下问题:

  1. 如果用户将第1个参数设置为NULL的话,那么将使用Net_Congfig.c文件中设置的NTP服务器地址。

使用举例:

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

**********************************************************************************************************

                                                   变量

**********************************************************************************************************

*/

U8 ntp_server[] = {,,,};  /* SNTP服务器地址 */

/*

*********************************************************************************************************

*    函 数 名: time_cback

*    功能说明: SNTP获取时间回到函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void time_cback (uint32_t time)

{

     struct tm *t_tm;

     if (time == )

     {

         printf_debug ("错误, 服务器未响应或者网络状态比较差\r\n");

     }

     else

     {

         /* 由于是格林尼治时间,所以北京时间要加8个小时 */

         time += **;

         /* 将获取的UNIX时间戳转换成年月日时分秒的格式 */

         t_tm = localtime((unsigned int *)&time);

         /* 格林尼治时间是从1900年开始的,这要加上 */

         t_tm->tm_year += ;

         /* 获取的月份范围是0-11,这里要加1 */

         t_tm->tm_mon += ;

         printf_debug ("UNIX时间戳:%d 日期:%02d/%02d/%02d  时间:%02d:%02d:%02d\r\n",

                        time,

                        t_tm->tm_year,

                          t_tm->tm_mon,

                          t_tm->tm_mday,

                          t_tm->tm_hour,

                          t_tm->tm_min,

                          t_tm->tm_sec);

     }

}                                   

/*

*********************************************************************************************************

*    函 数 名: get_time

*    功能说明: 从SNTP服务器获取当前时间

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/                      

static void get_time (void)

{

     if (sntp_get_time((U8 *)&ntp_server[], &time_cback) == __TRUE)

     {

         printf_debug ("SNTP请求已经发送成功\r\n");

     }

     else

     {

         printf_debug ("失败, SNTP未就绪或者参数错误\r\n");

     }

}

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

     main_TcpNet ();

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     /* 初始化网络协议栈 */

     init_TcpNet ();

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(, );

     /* 再创建一个周期是1000ms的软定时器 */

     bsp_StartAutoTimer(, );

     while ()

     {

         /* TCP轮询 */

         tcpnet_poll();

          /* 每秒从NTP服务器获取一次时间 */

         if(bsp_CheckTimer())

         {

              get_time();

         }

     }

}

30.4 SNTP配置说明(Net_Config.c)

(本章节配套例子的配置与本小节的说明相同)

RL-TCPnet的配置工作是通过配置文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

RL-TCPnet要配置的选项非常多,我们这里把几个主要的配置选项简单介绍下。

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

System Definitions

(1)Local Host Name

局域网域名。

这里起名为armfly,使用局域网域名限制为15个字符。

(2)Memory Pool size

参数范围1536-262144字节。

内存池大小配置,单位字节。另外注意一点,配置向导这里显示的单位是字节,如果看原始定义,MDK会做一个自动的4字节倍数转换,比如我们这里配置的是8192字节,那么原始定义是#define MEM_SIZE  2048,也就是8192/4 = 2048。

(3)Tick Timer interval

可取10,20,25,40,50,100,200,单位ms。

系统滴答时钟间隔,也就是网络协议栈的系统时间基准,默认情况下,取值100ms。

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

Ethernet Network Interface

以太网接口配置,勾选了此选项就可以配置了,如果没有使能DHCP的话,将使用这里配置的固定IP

(1) MAC Address

局域网内可以随意配置,只要不跟局域网内其它设备的MAC地址冲突即可。

(2)IP Address

IP地址。

(3)Subnet mask

子网掩码。

(4)Default Gateway

默认网关。

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

Ethernet Network Interface

以太网接口配置,这个配置里面还有如下两项比较重要的配置需要说明。

(1)NetBIOS Name Service

NetBIOS局域网域名服务,这里打上对勾就使能了。这样我们就可以通过前面配置的Local Host Name局域网域名进行访问,而不需要通过IP地址访问了。

(2)Dynaminc Host Configuration

即DHCP,这里打上对勾就使能了。使能了DHCP后,RL-TCPnet就可以从外接的路由器上获得动态IP地址。

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

UDP Sockets

UDP Sockets配置,打上对勾就使能了此项功能

(1) Number of UDP Sockets

用于配置可创建的UDP Sockets数量,这里配置了5个。

范围1 – 20。

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

TCP Sockets

TCP Sockets配置,打上对勾就使能了此项功能

(1) Number of TCP Sockets

用于配置可创建的TCP Sockets数量。

(2)Number of Retries

范围0-20。

用于配置重试次数,TCP数据传输时,如果在设置的重试时间内得不到应答,算一次重试失败,这里就是配置的最大重试次数。

(3)Retry Timeout in seconds

范围1-10,单位秒。

重试时间。如果发送的数据在重试时间内得不到应答,将重新发送数据。

(4)Default Connect Timeout in seconds

范围1-600,单位秒。

用于配置默认的保持连接时间,即我们常说的Keep Alive时间,如果时间到了将断开连接。常用于HTTP Server,Telnet Server等。

(5)Maximum Segment Size

范围536-1460,单位字节。

MSS定义了TCP数据包能够传输的最大数据分段。

(6)Receive Window Size

范围536-65535,单位字节。

TCP接收窗口大小。

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

SNTP Client

SNTP配置,打上对勾就使能了此项功能

(1) Broadcast Mode

打上对勾表示使能广播模式,不选择表示单播模式。

(2)NTP Server

这里是NTP服务器的IP地址。

实际应用中,这两个选项的作用看本章30.3.1小节的函数sntp_get_time即可。

30.5 SNTP调试说明(Net_Debug.c)

(重要说明,RL-TCPnet的调试是通过串口打印出来的)

RL-TCPnet的调试功能是通过配置文件Net_Debug.c实现。在MDK工程中打开文件Net_Debug.c,可以看到如下图所示的工程配置向导:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

Print Time Stamp

勾选了此选项的话,打印消息时,前面会附带时间信息。

其它所有的选项

默认情况下,所有的调试选项都关闭了,每个选项有三个调试级别可选择,这里我们以SNTP Debug为例,点击下拉列表,可以看到里面有Off,Errors only和Full debug三个调试级别可供选择,每个调试选项里面都是这三个级别。

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

Off:表示关闭此选项的调试功能。

Errors only:表示仅在此选项出错时,将其错误打印出来。

Full debug:表示此选项的全功能调试。

下面是对SNTP Debug配置为Full debug时,打印出来的消息(NTP服务器采用的182.16.3.162):

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

30.6 板子的操作步骤

本章的操作相对比较简单,用户务必将板子连接到能够联网的路由器或者交换机上。串口会每秒打印一次获取的UNIX时间戳(波特率115200,数据位8,奇偶校验位无,停止位1):

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

30.7 实验例程说明(裸机)

30.7.1 STM32F407开发板实验

配套例子:

V5-1040_RL-TCPnet实验SNTP应用(裸机)

实验目的:

  1. 学习RL-TCPnet的SNTP使用。

实验内容:

  1. 务必将网线接到能够联网的路由器或者交换机上面测试,因为本实验要用到外网。
  2. 本实验串口每秒打印一次从NTP服务器获取的时间。
  3. 国内免费稳定的SNTP服务器很少,当前使用的这个时好时坏,如果大家测试不成功的话也是正常的。

实验操作:

详见本章节30.6小节。

配置向导文件设置(Net_Config.c):

详见本章节30.4小节。

调试文件设置(Net_Debug.c):

详见本章节30.5小节。

程序设计:

主函数初始化

在main.c文件实现:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 进入RL-TCPnet测试函数 */

     TCPnetTest();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();     /* 初始LED指示灯端口 */

     bsp_InitTimer();   /* 初始化滴答定时器 */

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现从远程NTP服务器获取当前日期和时间,精度到秒。

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

**********************************************************************************************************

                                                   变量

**********************************************************************************************************

*/

U8 ntp_server[] = {,,,};  /* SNTP服务器地址 */

/*

*********************************************************************************************************

*    函 数 名: time_cback

*    功能说明: SNTP获取时间回到函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void time_cback (uint32_t time)

{

     struct tm *t_tm;

     if (time == )

     {

         printf_debug ("错误, 服务器未响应或者网络状态比较差\r\n");

     }

     else

     {

         /* 由于是格林尼治时间,所以北京时间要加8个小时 */

         time += **;

         /* 将获取的UNIX时间戳转换成年月日时分秒的格式 */

         t_tm = localtime((unsigned int *)&time);

         /* 格林尼治时间是从1900年开始的,这要加上 */

         t_tm->tm_year += ;

         /* 获取的月份范围是0-11,这里要加1 */

         t_tm->tm_mon += ;

         printf_debug ("UNIX时间戳:%d 日期:%02d/%02d/%02d  时间:%02d:%02d:%02d\r\n",

                        time,

                        t_tm->tm_year,

                          t_tm->tm_mon,

                          t_tm->tm_mday,

                          t_tm->tm_hour,

                          t_tm->tm_min,

                          t_tm->tm_sec);

     }

}                                   

/*

*********************************************************************************************************

*    函 数 名: get_time

*    功能说明: 从SNTP服务器获取当前时间

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/                      

static void get_time (void)

{

     if (sntp_get_time((U8 *)&ntp_server[], &time_cback) == __TRUE)

     {

         printf_debug ("SNTP请求已经发送成功\r\n");

     }

     else

     {

         printf_debug ("失败, SNTP未就绪或者参数错误\r\n");

     }

}

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

     main_TcpNet ();

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     /* 初始化网络协议栈 */

     init_TcpNet ();

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(, );

     /* 再创建一个周期是1000ms的软定时器 */

     bsp_StartAutoTimer(, );

     while ()

     {

         /* TCP轮询 */

         tcpnet_poll();

          /* 每秒从NTP服务器获取一次时间 */

         if(bsp_CheckTimer())

         {

              get_time();

         }

     }

}

30.7.2 STM32F429开发板实验

配套例子:

V6-1040_RL-TCPnet实验SNTP应用(裸机)

实验目的:

  1. 学习RL-TCPnet的SNTP使用。

实验内容:

  1. 务必将网线接到能够联网的路由器或者交换机上面测试,因为本实验要用到外网。
  2. 本实验串口每秒打印一次从NTP服务器获取的时间。
  3. 国内免费稳定的SNTP服务器很少,当前使用的这个时好时坏,如果大家测试不成功的话也是正常的。

实验操作:

详见本章节30.6小节。

配置向导文件设置(Net_Config.c):

详见本章节30.4小节。

调试文件设置(Net_Debug.c):

详见本章节30.5小节

程序设计:

主函数初始化

在main.c文件实现:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 进入RL-TCPnet测试函数 */

     TCPnetTest();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();     /* 初始LED指示灯端口 */

     bsp_InitTimer();   /* 初始化滴答定时器 */

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现从远程NTP服务器获取当前日期和时间,精度到秒。

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

**********************************************************************************************************

                                                   变量

**********************************************************************************************************

*/

U8 ntp_server[] = {,,,};  /* SNTP服务器地址 */

/*

*********************************************************************************************************

*    函 数 名: time_cback

*    功能说明: SNTP获取时间回到函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void time_cback (uint32_t time)

{

     struct tm *t_tm;

     if (time == )

     {

         printf_debug ("错误, 服务器未响应或者网络状态比较差\r\n");

     }

     else

     {

         /* 由于是格林尼治时间,所以北京时间要加8个小时 */

         time += **;

         /* 将获取的UNIX时间戳转换成年月日时分秒的格式 */

         t_tm = localtime((unsigned int *)&time);

         /* 格林尼治时间是从1900年开始的,这要加上 */

         t_tm->tm_year += ;

         /* 获取的月份范围是0-11,这里要加1 */

         t_tm->tm_mon += ;

         printf_debug ("UNIX时间戳:%d 日期:%02d/%02d/%02d  时间:%02d:%02d:%02d\r\n",

                        time,

                        t_tm->tm_year,

                          t_tm->tm_mon,

                          t_tm->tm_mday,

                          t_tm->tm_hour,

                          t_tm->tm_min,

                          t_tm->tm_sec);

     }

}                                   

/*

*********************************************************************************************************

*    函 数 名: get_time

*    功能说明: 从SNTP服务器获取当前时间

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/                      

static void get_time (void)

{

     if (sntp_get_time((U8 *)&ntp_server[], &time_cback) == __TRUE)

     {

         printf_debug ("SNTP请求已经发送成功\r\n");

     }

     else

     {

         printf_debug ("失败, SNTP未就绪或者参数错误\r\n");

     }

}

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

     main_TcpNet ();

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     /* 初始化网络协议栈 */

     init_TcpNet ();

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(, );

     /* 再创建一个周期是1000ms的软定时器 */

     bsp_StartAutoTimer(, );

     while ()

     {

         /* TCP轮询 */

         tcpnet_poll();

          /* 每秒从NTP服务器获取一次时间 */

         if(bsp_CheckTimer())

         {

              get_time();

         }

     }

}

30.8 实验例程说明(RTX)

30.8.1 STM32F407开发板实验

配套例子:

V5-1041_RL-TCPnet实验SNTP应用(RTX)

实验目的:

  1. 学习RL-TCPnet的SNTP使用。

实验内容:

  1. 务必将网线接到能够联网的路由器或者交换机上面测试,因为本实验要用到外网。
  2. 本实验串口每秒打印一次从NTP服务器获取的时间。
  3. 国内免费稳定的SNTP服务器很少,当前使用的这个时好时坏,如果大家测试不成功的话也是正常的。

实验操作:

详见本章节30.6小节。

配置向导文件设置(Net_Config.c):

详见本章节30.4小节。

调试文件设置(Net_Debug.c):

详见本章节30.5小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

Task Configuration

(1)Number of concurrent running tasks

允许创建6个任务,实际创建了如下5个任务:

AppTaskUserIF任务   :按键消息处理。

AppTaskLED任务     :LED闪烁。

AppTaskMsgPro任务 :按键检测。

AppTaskTCPMain任务:RL-TCPnet测试任务。

AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

(2)Number of tasks with user-provided stack

创建的5个任务都是采用自定义堆栈方式。

(3)Run in privileged mode

设置任务运行在非特权级模式。

RTX任务调试信息:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

程序设计:

任务栈大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */

static uint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */

static uint64_t AppTaskStartStk[1024/8];     /* 任务栈 */

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。

系统栈大小分配:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

RTX初始化:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 创建启动任务 */

     os_sys_init_user (AppTaskStart,              /* 任务函数 */

                       ,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

                       sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */

     while();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     bsp_InitDWT();     /* 初始化DWT */

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();    /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();    /* 初始LED指示灯端口 */

}

RTX任务创建:

/*

*********************************************************************************************************

*    函 数 名: AppTaskCreate

*    功能说明: 创建应用任务

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */

                                        ,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

                                           sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */

}

五个RTX任务的实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskUserIF

*    功能说明: 按键消息处理     

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)

*********************************************************************************************************

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

    while()

    {

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下 */

                   case KEY_DOWN_K1:

                       printf("K1键按下 \r\n");       

                       break;  

                   /* K2键按下 */

                   case KEY_DOWN_K2:

                       printf("K2键按下 \r\n");

                       break;

                   /* K3键按下 */

                   case KEY_DOWN_K3:

                       printf("K3键按下 \r\n");

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

         os_dly_wait();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

*********************************************************************************************************

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = ; /* 延迟周期 */

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    while()

    {

         bsp_LedToggle();

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/

         os_itv_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

*********************************************************************************************************

*/

__task void AppTaskMsgPro(void)

{

    while()

    {

         bsp_KeyScan();

         os_dly_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while ()

     {

         TCPnetTest();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

     /* 创建任务 */

     AppTaskCreate();

     os_itv_set ();

    while()

    {

         os_itv_wait ();

         /* RL-TCPnet时间基准更新函数 */

         timer_tick ();

    }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现从远程NTP服务器获取当前日期和时间,精度到秒。

#include "includes.h"

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

**********************************************************************************************************

                                                   变量

**********************************************************************************************************

*/

U8 ntp_server[] = {,,,};  /* SNTP服务器地址 */

/*

*********************************************************************************************************

*    函 数 名: time_cback

*    功能说明: SNTP获取时间回到函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void time_cback (uint32_t time)

{

     struct tm *t_tm;

     if (time == )

     {

         printf_debug ("错误, 服务器未响应或者网络状态比较差\r\n");

     }

     else

     {

          /* 由于是格林尼治时间,所以北京时间要加8个小时 */

         time += **;

          /* 将获取的UNIX时间戳转换成年月日时分秒的格式 */

         t_tm = localtime((unsigned int *)&time);

          /* 格林尼治时间是从1900年开始的,这要加上 */

         t_tm->tm_year += ;

         /* 获取的月份范围是0-11,这里要加1 */

         t_tm->tm_mon += ;

         printf_debug ("UNIX时间戳:%d 日期:%02d/%02d/%02d  时间:%02d:%02d:%02d\r\n",

                        time,

                        t_tm->tm_year,

                          t_tm->tm_mon,

                          t_tm->tm_mday,

                          t_tm->tm_hour,

                          t_tm->tm_min,

                          t_tm->tm_sec);

     }

}   

/*

*********************************************************************************************************

*    函 数 名: get_time

*    功能说明: 从SNTP服务器获取当前时间

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/                       

static void get_time (void)

{

     if (sntp_get_time((U8 *)&ntp_server[], &time_cback) == __TRUE)

     {

         printf_debug ("SNTP请求已经发送成功\r\n");

     }

     else

     {

         printf_debug ("失败, SNTP未就绪或者参数错误\r\n");

     }

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     uint32_t tstart, tend;

     /* 初始化起始时间 */  

     tstart = os_time_get();

     while ()

     {

          /* RL-TCPnet处理函数 */

         main_TcpNet();

         /* 每秒从NTP服务器获取一次时间 */       

         tend = os_time_get() - tstart;

         if(tend >= )

         {

              tstart = os_time_get();

              get_time();

         }

         os_dly_wait();

     }

}

30.8.2 STM32F429开发板实验

配套例子:

V6-1041_RL-TCPnet实验SNTP应用(RTX)

实验目的:

  1. 学习RL-TCPnet的SNTP使用。

实验内容:

  1. 务必将网线接到能够联网的路由器或者交换机上面测试,因为本实验要用到外网。
  2. 本实验串口每秒打印一次从NTP服务器获取的时间。
  3. 国内免费稳定的SNTP服务器很少,当前使用的这个时好时坏,如果大家测试不成功的话也是正常的。

实验操作:

详见本章节30.6小节。

配置向导文件设置(Net_Config.c):

详见本章节30.4小节。

调试文件设置(Net_Debug.c):

详见本章节30.5小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

Task Configuration

(1)Number of concurrent running tasks

允许创建6个任务,实际创建了如下5个任务:

AppTaskUserIF任务   :按键消息处理。

AppTaskLED任务     :LED闪烁。

AppTaskMsgPro任务 :按键检测。

AppTaskTCPMain任务:RL-TCPnet测试任务。

AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

(2)Number of tasks with user-provided stack

创建的5个任务都是采用自定义堆栈方式。

(3)Run in privileged mode

设置任务运行在非特权级模式。

RTX任务调试信息:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

程序设计:

任务栈大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */

static uint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */

static uint64_t AppTaskStartStk[1024/8];     /* 任务栈 */

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。

系统栈大小分配:

【RL-TCPnet网络教程】第30章     RL-TCPnet之SNTP网络时间获取

RTX初始化:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 创建启动任务 */

     os_sys_init_user (AppTaskStart,              /* 任务函数 */

                       ,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

                       sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */

     while();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */

     bsp_InitDWT();      /* 初始化DWT */

     bsp_InitUart();     /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitExtIO();    /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */

     bsp_InitLed();      /* 初始LED指示灯端口 */

}

RTX任务创建:

/*

*********************************************************************************************************

*    函 数 名: AppTaskCreate

*    功能说明: 创建应用任务

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */

                                        ,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

                                           sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */

}

五个RTX任务的实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskUserIF

*    功能说明: 按键消息处理     

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)

*********************************************************************************************************

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

    while()

    {

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下 */

                   case KEY_DOWN_K1:

                       printf("K1键按下 \r\n");       

                       break;  

                   /* K2键按下 */

                   case KEY_DOWN_K2:

                       printf("K2键按下 \r\n");

                       break;

                   /* K3键按下 */

                   case KEY_DOWN_K3:

                       printf("K3键按下 \r\n");

                       break;

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

         os_dly_wait();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

*********************************************************************************************************

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = ; /* 延迟周期 */

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    while()

    {

         bsp_LedToggle();

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/

         os_itv_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

*********************************************************************************************************

*/

__task void AppTaskMsgPro(void)

{

    while()

    {

         bsp_KeyScan();

         os_dly_wait();

    }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while ()

     {

         TCPnetTest();

     }

}

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

     /* 创建任务 */

     AppTaskCreate();

     os_itv_set ();

    while()

    {

         os_itv_wait ();

         /* RL-TCPnet时间基准更新函数 */

         timer_tick ();

    }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现从远程NTP服务器获取当前日期和时间,精度到秒。

#include "includes.h"

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

/*

**********************************************************************************************************

                                                   变量

**********************************************************************************************************

*/

U8 ntp_server[] = {,,,};  /* SNTP服务器地址 */

/*

*********************************************************************************************************

*    函 数 名: time_cback

*    功能说明: SNTP获取时间回到函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void time_cback (uint32_t time)

{

     struct tm *t_tm;

     if (time == )

     {

         printf_debug ("错误, 服务器未响应或者网络状态比较差\r\n");

     }

     else

     {

          /* 由于是格林尼治时间,所以北京时间要加8个小时 */

         time += **;

          /* 将获取的UNIX时间戳转换成年月日时分秒的格式 */

         t_tm = localtime((unsigned int *)&time);

          /* 格林尼治时间是从1900年开始的,这要加上 */

         t_tm->tm_year += ;

         /* 获取的月份范围是0-11,这里要加1 */

         t_tm->tm_mon += ;

         printf_debug ("UNIX时间戳:%d 日期:%02d/%02d/%02d  时间:%02d:%02d:%02d\r\n",

                        time,

                        t_tm->tm_year,

                          t_tm->tm_mon,

                          t_tm->tm_mday,

                          t_tm->tm_hour,

                          t_tm->tm_min,

                          t_tm->tm_sec);

     }

}   

/*

*********************************************************************************************************

*    函 数 名: get_time

*    功能说明: 从SNTP服务器获取当前时间

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/                       

static void get_time (void)

{

     if (sntp_get_time((U8 *)&ntp_server[], &time_cback) == __TRUE)

     {

         printf_debug ("SNTP请求已经发送成功\r\n");

     }

     else

     {

         printf_debug ("失败, SNTP未就绪或者参数错误\r\n");

     }

}

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     uint32_t tstart, tend;

     /* 初始化起始时间 */  

     tstart = os_time_get();

     while ()

     {

          /* RL-TCPnet处理函数 */

         main_TcpNet();

         /* 每秒从NTP服务器获取一次时间 */       

         tend = os_time_get() - tstart;

         if(tend >= )

         {

              tstart = os_time_get();

              get_time();

         }

         os_dly_wait();

     }

}

30.9 总结

本章节就为大家讲解这么多,本章节的内容相对比较简单,主要是函数sntp_get_time的使用,希望大家熟练掌握。