【前言】
STM32H7S78-DK移植LWIP-电子产品世界论坛 (eepw.com.cn)
在实现连网络连接后,移植TouchGFX,动态获取RTC时间,以及环境温湿度,并实现动态展示。
1、添加RTC外设,在STM32CubeMX中使能RTC:
添加RTC时间初始化后,系统会自动生成RTC的初始化。
2、添加tcpclient.c/h文件。代码如下:
/* * tcpclinet.c * * Created on: Mar 17, 2024 * Author: liujianhua */ #include "lwip/netif.h" #include "lwip/ip.h" #include "lwip/tcp.h" #include "lwip/init.h" #include "netif/etharp.h" #include "lwip/udp.h" #include "lwip/pbuf.h" #include <time.h> #include <stdio.h> #include <string.h> #include "main.h" #include "SHT3x.h" void TCP_Client_Init(void); uint8_t send_buf_led1on[] = "led1on\n"; uint8_t send_buf_led2on[] = "led2on\n"; uint8_t send_buf_led1off[] = "led1off\n"; uint8_t send_buf_led2off[] = "led2off\n"; uint8_t send_buf_err[] = "errcmd\n"; extern RTC_HandleTypeDef hrtc; extern SHT3x_ReadData SHT3x_Data; int String2Int(char *str);//字符串转数字 void sntp_set_time(uint32_t sntp_time); static void client_err(void *arg, err_t err) //出现错误时调用这个函数,打印错误信息,并尝试重新连接 { printf("连接错误!!\n"); printf("尝试重连!!\n"); //连接失败的时候释放TCP控制块的内存 printf("关闭连接,释放TCP控制块内存\n"); //tcp_close(client_pcb); //重新连接 printf("重新初始化客户端\n"); TCP_Client_Init(); } static err_t client_send(void *arg, struct tcp_pcb *tpcb) //发送函数,调用了tcp_write函数 { uint8_t send1_buf[48]= {0}; //发送数据到服务器 sprintf(send1_buf,"Tem:%.1f,Hum:%.1f",SHT3x_Data.SHT3x_Temperature,SHT3x_Data.SHT3x_Humidity); tcp_write(tpcb, send1_buf, strlen(send1_buf), 1); return ERR_OK; } static err_t client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { uint32_t set_time; if (p != NULL) { /* 接收数据*/ tcp_recved(tpcb, p->tot_len); printf("收到:%s 长度为:%d\n",p->payload,p->tot_len); if((p->tot_len) == 7 ) { if(strncmp("LED1OFF",p->payload,p->tot_len) == 0) { // HAL_GPIO_WritePin(USER_LED1_GPIO_Port, USER_LED1_Pin, GPIO_PIN_SET); tcp_write(tpcb, send_buf_led1off, sizeof(send_buf_led1off), 1); } else if(strncmp("LED2OFF",p->payload,p->tot_len) == 0) { // HAL_GPIO_WritePin(USER_LED2_GPIO_Port, USER_LED2_Pin, GPIO_PIN_SET); tcp_write(tpcb, send_buf_led2off, sizeof(send_buf_led2off), 1); } } else if((p->tot_len) == 6) { if(strncmp("LED1ON",p->payload,p->tot_len) == 0) { // HAL_GPIO_WritePin(USER_LED1_GPIO_Port, USER_LED1_Pin, GPIO_PIN_RESET); tcp_write(tpcb, send_buf_led1on, sizeof(send_buf_led1on), 1); } else if(strncmp("LED2ON",p->payload,p->tot_len) == 0) { // HAL_GPIO_WritePin(USER_LED1_GPIO_Port, USER_LED2_Pin, GPIO_PIN_RESET); tcp_write(tpcb, send_buf_led2on, sizeof(send_buf_led2on), 1); } } else if((p->tot_len) == 12) { set_time = String2Int(p->payload); sntp_set_time(set_time); } else { tcp_write(tpcb, send_buf_err, sizeof(send_buf_err), 1); } /* 返回接收到的数据*/ memset(p->payload, 0 , p->tot_len); pbuf_free(p); } else if (err == ERR_OK) { //服务器断开连接 printf("服务器断开连接!\n"); tcp_close(tpcb); //重新连接 TCP_Client_Init(); } return ERR_OK; } static err_t client_connected(void *arg, struct tcp_pcb *pcb, err_t err) { printf("connected ok!\n"); //注册一个周期性回调函数 tcp_poll(pcb,client_send,2); //注册一个接收函数 tcp_recv(pcb,client_recv); return ERR_OK; } void TCP_Client_Init(void) { struct tcp_pcb *client_pcb = NULL; //这一句一定要放在里面,否则会没用 ip4_addr_t server_ip; //因为客户端要主动去连接服务器,所以要知道服务器的IP地址 /* 创建一个TCP控制块 */ client_pcb = tcp_new(); IP4_ADDR(&server_ip, 192,168,3,180);//合并IP地址 printf("客户端开始连接!\n"); //开始连接 tcp_connect(client_pcb, &server_ip, 777, client_connected); ip_set_option(client_pcb, SOF_KEEPALIVE); printf("已经调用了tcp_connect函数\n"); //注册异常处理 tcp_err(client_pcb, client_err); printf("已经注册异常处理函数\n"); } /*! * @brief SNTP 获取时间戳的处理函数 * 执行条件:无 * * @param [in] : sntp 获取的时间戳 * * @retval: 无 */ 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); 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 %d:%d:%d\n", \ sDate.Year, sDate.Month, sDate.Date, sTime.Hours, sTime.Minutes, sTime.Seconds); printf("sntp_set_time: c04, set rtc time done\n"); } int String2Int(char *str)//字符串转数字 { char flag = '+';//指示结果是否带符号 long res = 0; if(*str=='-')//字符串带负号 { ++str;//指向下一个字符 flag = '-';//将标志设为负号 } //逐个字符转换,并累加到结果res while(*str>=48 && *str<=57)//如果是数字才进行转换,数字0~9的ASCII码:48~57 { res = 10*res+ *str++-48;//字符'0'的ASCII码为48,48-48=0刚好转化为数字0 } if(flag == '-')//处理是负数的情况 { res = -res; } printf("chang:%d",res); return (int)res; }
这个函数中,主要实现的功能为创建一个client_pcb,并绑定到指定的端口上。
注册一个连接成功的static err_t client_connected(void *arg, struct tcp_pcb *pcb, err_t err)。在连接成功之后,注册一个周期发送数据包的函数static err_t client_send(void *arg, struct tcp_pcb *tpcb) //发送函数,调用了tcp_write函数。用tcp_poll进行调用,同时注册一个tcp_recv函数,用于处理接收数据的功能。
还需要注册一个连接错误的处理函数,用重新连接服务器。
在接收处理函数中,判断一下接收到的数据,我这里添加了一个用于处理设置时间的set_time函数,用于设置RTC时间,这样就可以通过tcp_server发送一个时间戳记,来实现远程的时间同步。
同时,我添加了一个sht3x的驱动库,用于驱动一个sht30的温湿度处理,在周期发送函数中,发送实时的湿湿度。
【TouchGFX】
在TouchGFX中,添加一个用于展示数据的屏幕,添加两个gauge,一个显示温度,一个显示湿度,同时添加日期、时间、IP地址。
【代码添加】
在screenview.cpp中添加handleEvent周期函数,用于动态展示IP地址、日期时间,温湿度。
代码如下:
void ScreenVaccView::handleTickEvent () { int sht3x_sta; tickCounter++; if(tickCounter>6000) { tickCounter = 0; } if (tickCounter % 60 == 0) { HAL_RTC_GetTime(&hrtc, &gSystemTime, RTC_FORMAT_BIN);//获取时间 /* Get the RTC current Date */ HAL_RTC_GetDate(&hrtc, &gSystemDate, RTC_FORMAT_BIN);//获取日期 Unicode::snprintf(textDateBuffer, TEXTDATE_SIZE, "%4d-%02d-%02d", 2000 + gSystemDate.Year,gSystemDate.Month, gSystemDate.Date); textDate.invalidate(); digitalClock.setTime24Hour(gSystemTime.Hours, gSystemTime.Minutes, gSystemTime.Seconds); } if (tickCounter % 120 == 0) { sht3x_sta = SHT3x_Get_TemperatureHumidity(); if(0 == sht3x_sta) { gaugeTmp.updateValue(SHT3x_Data.SHT3x_Temperature, 30); gaugeHum.updateValue(SHT3x_Data.SHT3x_Humidity, 30); Unicode::snprintfFloat(textTmpBuffer,20,"%.1f",SHT3x_Data.SHT3x_Temperature); Unicode::snprintfFloat(textHumBuffer,20,"%.1f",SHT3x_Data.SHT3x_Humidity); textTmp.invalidate(); textHum.invalidate(); } } }
实现的效果: