从NTP服务器获取时钟校准MCU的RTC时间,是一种获取准确时间的方法。
本文通过WB20-12F来实现这一过程

一、NTP获取时间
1、变量定义
#define TIME_MODE 1 //0: for UTC with microseconds, 1: for timezone with seconds
#define SNTP_SERVER "pool.ntp.org"
2、rtc初始化
static void rtc_init(void)
{
RTC_InitTypeDef RTC_InitStruct;
RCC_PeriphClockCmd(APBPeriph_RTC, APBPeriph_RTC_CLOCK, ENABLE);
RTC_StructInit(&RTC_InitStruct);
RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24;
RTC_Init(&RTC_InitStruct);
/* set UTC+8 localtime */
setenv("TZ", "CST-8", 1);
}
3、设置rtc时间函数
static void set_rtc_time(struct tm *ti)
{
struct tm *timeinfo;
timeinfo=ti;
RTC_TimeTypeDef RTC_TimeStruct;
/*set time in RTC */
RTC_TimeStructInit(&RTC_TimeStruct);
RTC_TimeStruct.RTC_H12_PMAM = RTC_H12_AM;
RTC_TimeStruct.RTC_Days = timeinfo->tm_yday;
RTC_TimeStruct.RTC_Hours = timeinfo->tm_hour;
RTC_TimeStruct.RTC_Minutes = timeinfo->tm_min;
RTC_TimeStruct.RTC_Seconds = timeinfo->tm_sec;
RTC_TimeStruct.RTC_Year = timeinfo->tm_year + 1900;
RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct);
}
4、获取NTP时间并更新RTC
tatic void show_time(void)
{
#if (TIME_MODE == 0)
uint32_t sec;
uint32_t us;
SNTP_GET_SYSTEM_TIME(sec, us);
time_t now = (time_t)sec;
RTK_LOGS(NOTAG, RTK_LOG_INFO, "%s + %d usec\n", ctime(&now), us);
#elif (TIME_MODE == 1)
time_t now;
struct tm timeinfo;
int timezone = 8; // use UTC+8(offset in hrs) timezone for example, 8 * 60 * 60(offset in seconds)
time(&now);
setenv("TZ", "CST-8", 1);
tzset();
timeinfo = *(localtime(&now));
RTK_LOGS(NOTAG, RTK_LOG_INFO, "%d-%d-%d %d:%d:%d UTC%s%d\n",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
(timezone > 0) ? "+" : "", timezone);
set_rtc_time(&timeinfo);
#endif
}
5、线程函数
获取10次NTP时间后,退出线程。
在开始前,需要通过调试串口发送AT指令连接WIFI:
AT+WLCONN=ssid,你的ssid,pw,SSID的秘密
static void example_sntp_showtime_thread(void *param)
{
/* To avoid gcc warnings */
(void) param;
int should_stop = 1;
int get_time=10;
// Delay to check successful WiFi connection and obtain of an IP address
LwIP_Check_Connectivity();
rtc_init();
RTK_LOGS(NOTAG, RTK_LOG_INFO, "\r\n====================Example: SNTP show time====================\r\n");
sntp_setservername(0, SNTP_SERVER);
sntp_init();
while (1) {
show_time();
rtos_time_delay_ms(1000);
if (!get_time&&should_stop) {
break;
}
get_time--;
}
rtos_task_delete(NULL);
}
二、LCD驱动显示RTC时间
1、获取rtc时间
static struct tm *get_rtc_time(void )
{
RTC_TimeTypeDef RTC_TimeStruct;
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
time_t seconds = time(NULL);
struct tm *timeinfo = localtime(&seconds);
timeinfo->tm_sec = RTC_TimeStruct.RTC_Seconds;
timeinfo->tm_min = RTC_TimeStruct.RTC_Minutes;
timeinfo->tm_hour = RTC_TimeStruct.RTC_Hours;
timeinfo->tm_year = RTC_TimeStruct.RTC_Year - 1900;
// ti=timeinfo;
return timeinfo;
}
2、初始化LCD gpio
void init_gpio(void)
{
GPIO_InitTypeDef GPIO_InitStruct_LCD;
GPIO_InitStruct_LCD.GPIO_Pin = LCD_RST;
GPIO_InitStruct_LCD.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(&GPIO_InitStruct_LCD);
GPIO_InitStruct_LCD.GPIO_Pin = LCD_DC;
GPIO_InitStruct_LCD.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(&GPIO_InitStruct_LCD);
#ifndef HARDWARE_SPI
GPIO_InitStruct_LCD.GPIO_Pin = LCD_SDA;
GPIO_InitStruct_LCD.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(&GPIO_InitStruct_LCD);
GPIO_InitStruct_LCD.GPIO_Pin = LCD_SCL;
GPIO_InitStruct_LCD.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(&GPIO_InitStruct_LCD);
GPIO_InitStruct_LCD.GPIO_Pin = LCD_CS;
GPIO_InitStruct_LCD.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(&GPIO_InitStruct_LCD);
#endif
}
3、硬件SPI初始化
void init_spi(void)
{
u32 SclkPhase = SCPH_TOGGLES_IN_MIDDLE; // SCPH_TOGGLES_IN_MIDDLE or SCPH_TOGGLES_AT_START
u32 SclkPolarity = SCPOL_INACTIVE_IS_LOW; // SCPOL_INACTIVE_IS_LOW or SCPOL_INACTIVE_IS_HIGH
u32 ClockDivider = 20; // Fssi_clk is 100MHz
rtos_time_delay_ms(500);
Pinmux_Swdoff();
/* SPI1 is as Master and SPI0 is as Slave */
/* init SPI1 */
SSI_SetRole(SPI1_DEV, SSI_MASTER);
SSI_InitTypeDef SSI_InitStructM;
SSI_StructInit(&SSI_InitStructM);
RCC_PeriphClockCmd(APBPeriph_SPI1, APBPeriph_SPI1_CLOCK, ENABLE);
Pinmux_Config(SPI1_MOSI, PINMUX_FUNCTION_SPIM);
Pinmux_Config(SPI1_MISO, PINMUX_FUNCTION_SPIM);
Pinmux_Config(SPI1_SCLK, PINMUX_FUNCTION_SPIM);
Pinmux_Config(SPI1_CS, PINMUX_FUNCTION_SPIM);
PAD_PullCtrl((u32)SPI1_CS, GPIO_PuPd_UP);
SSI_InitStructM.SPI_Role = SSI_MASTER;
SSI_Init(SPI1_DEV, &SSI_InitStructM);
/* set format */
SSI_SetSclkPhase(SPI1_DEV, SclkPhase);
SSI_SetSclkPolarity(SPI1_DEV, SclkPolarity);
SSI_SetDataFrameSize(SPI1_DEV, DFS_8_BITS);
/* set frequency */
SSI_SetBaudDiv(SPI1_DEV, ClockDivider); // Fssi_clk is 100MHz
}
引脚定义为:
// SPI1 FID=8 & Fully PG
#define SPI1_MOSI _PB_19
#define SPI1_MISO _PB_20
#define SPI1_SCLK _PB_18
#define SPI1_CS _PB_21
#define LCD_RST _PA_8
#define LCD_DC _PA_12
#define LCD_SDA _PB_19
#define LCD_SCL _PB_18
#define LCD_CS _PB_21
#define HARDWARE_SPI
#define LCD_RST_SET GPIO_WriteBit(LCD_RST, 1)
#define LCD_RST_CLR GPIO_WriteBit(LCD_RST, 0)
#define LCD_DC_SET GPIO_WriteBit(LCD_DC, 1)
#define LCD_DC_CLR GPIO_WriteBit(LCD_DC, 0)
// #define LCD_CS_SET PAD_PullCtrl((u32)SPI1_CS, GPIO_PuPd_UP);
// #define LCD_CS_CLR PAD_PullCtrl((u32)SPI1_CS, GPIO_PuPd_DOWN);
#define LCD_CS_SET GPIO_WriteBit(LCD_CS, 1)
#define LCD_CS_CLR GPIO_WriteBit(LCD_CS, 0)
#define LCD_SDA_SET GPIO_WriteBit(LCD_SDA, 1)
#define LCD_SDA_CLR GPIO_WriteBit(LCD_SDA, 0)
#define LCD_SCL_SET GPIO_WriteBit(LCD_SCL, 1)
#define LCD_SCL_CLR GPIO_WriteBit(LCD_SCL, 0)
4、LCD线程任务
void lcd_task(void)
{
struct tm *timeinfo;
char str_lcd_time[20];
RTK_LOGS(NOTAG, RTK_LOG_INFO, "[%s] into lcd_task \r\n", __FUNCTION__);
init_gpio();
#ifdef HARDWARE_SPI
init_spi();
#endif
Lcd_Init();
Lcd_Clear(BLACK);
while(1)
{
timeinfo=get_rtc_time();
RTK_LOGS(NOTAG, RTK_LOG_INFO, "Time as a custom formatted string = %d-%d-%d %d:%d:%d\n\n",
timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour,
timeinfo->tm_min, timeinfo->tm_sec);
sprintf(str_lcd_time,"%04d-%02d-%02d",timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday);
Gui_DrawFont_GBK16(24,10,BLUE,GRAY0,str_lcd_time);
sprintf(str_lcd_time,"%02d:%02d:%02d",timeinfo->tm_hour,timeinfo->tm_min, timeinfo->tm_sec);
Gui_DrawFont_GBK16(24,36,BLUE,GRAY0,str_lcd_time);
rtos_time_delay_ms(1000);
}
//never excute
SSI_Cmd(SPI1_DEV, DISABLE);
rtos_task_delete(NULL);
}
5、LCD写数据函数
#ifdef HARDWARE_SPI
void SPI_WriteData(unsigned char Data)
{
while (!SSI_Writeable(SPI1_DEV));
//RTK_LOGS(NOTAG, RTK_LOG_INFO, "[%s] SPI_WriteData \r\n", __FUNCTION__);
SSI_WriteData(SPI1_DEV, Data);
/* master read */
while (!SSI_Readable(SPI1_DEV));
SSI_ReadData(SPI1_DEV);
}
#endif
写寄存器、写数据
void Lcd_WriteIndex(unsigned char Index)
{
LCD_DC_CLR;
#ifndef HARDWARE_SPI
LCD_CS_CLR;
#endif
SPI_WriteData(Index);
#ifndef HARDWARE_SPI
LCD_CS_SET;
#endif
}
void Lcd_WriteData(unsigned char Data)
{
LCD_DC_SET;
#ifndef HARDWARE_SPI
LCD_CS_CLR;
#endif
SPI_WriteData(Data);
#ifndef HARDWARE_SPI
LCD_CS_SET;
#endif
}