- /*
 
-  * ESP8266_NTP_Time_Clock.ino
 
-  * NTP Time Clock at ESP8266
 
-  *
 
-  */
 
 
- #include <TimeLib.h>
 
- #include <ESP8266WiFiMulti.h>
 
- #include <WiFiUdp.h>
 
 
- #define DEBUG_MODE 0
 
 
- ESP8266WiFiMulti wifiMulti;
 
- // WiFi connect timeout per AP. Increase when connecting takes longer.
 
- const uint32_t connectTimeoutMs = 5000;
 
 
- const char ssid[] = "OpenBSD";  //  your network SSID (name)
 
- const char pass[] = "********";       // your network password
 
 
- // NTP Servers:
 
- static const char ntpServerName[] = "ntp.aliyun.com";
 
 
- // TimeZone
 
- const int timeZone = 8;     // CST +8
 
 
 
- WiFiUDP Udp;
 
- unsigned int localPort = 8888;  // local port to listen for UDP packets
 
 
- static const uint8_t crc_table[] = {
 
-     0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31,
 
-     0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
 
-     0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9,
 
-     0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
 
-     0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1,
 
-     0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
 
-     0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe,
 
-     0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
 
-     0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16,
 
-     0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
 
-     0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80,
 
-     0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
 
-     0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8,
 
-     0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
 
-     0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10,
 
-     0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
 
-     0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f,
 
-     0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
 
-     0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
 
-     0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
 
-     0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef,
 
-     0xfa, 0xfd, 0xf4, 0xf3
 
- };
 
 
- time_t getNtpTime();
 
- void digitalClockDisplay();
 
- void printDigits(int digits);
 
- void sendNTPpacket(IPAddress &address);
 
- uint8_t crc8(uint8_t *p, uint8_t len);
 
 
- void setup()
 
- {
 
-   WiFi.persistent(false);
 
 
-   Serial.begin(115200);
 
-   while (!Serial) ; // Needed for Leonardo only
 
 
-   delay(250);
 
-   Serial.println("I:ESP8266 NTP Time Clock");
 
-   Serial.print("I:Connecting to ");
 
-   Serial.println(ssid);
 
 
-   // WiFi.begin(ssid, pass);
 
-   // while (WiFi.status() != WL_CONNECTED) {
 
-   //   delay(500);
 
-   //   Serial.print(".");
 
-   // }
 
 
-   WiFi.mode(WIFI_STA);
 
-   wifiMulti.addAP("OpenBSD", "13581882013");
 
-   while (wifiMulti.run(connectTimeoutMs) != WL_CONNECTED) {
 
-     delay(500);    
 
-     Serial.print(".");
 
-   }
 
-   Serial.println("OK");
 
 
-   Serial.print("I:IP address is ");
 
-   Serial.println(WiFi.localIP());
 
-   Serial.println("I:Starting UDP");
 
-   Udp.begin(localPort);
 
-   Serial.print("I:Local port: ");
 
-   Serial.println(Udp.localPort());
 
-   Serial.println("I:waiting for sync");
 
-   setSyncProvider(getNtpTime);
 
-   setSyncInterval(300);
 
- }
 
 
- time_t prevDisplay = 0; // when the digital clock was displayed
 
 
- void loop()
 
- {
 
-   if (timeStatus() != timeNotSet) {
 
-     if (now() != prevDisplay) { //update the display only if time has changed
 
-       prevDisplay = now();
 
-       digitalClockDisplay();
 
-     }
 
-   }
 
- }
 
 
- uint8_t crc8(char *p, uint8_t len)
 
- {
 
-         uint16_t i;
 
-         uint16_t crc = 0x0;
 
 
-         while (len--) {
 
-                 i = (crc ^ *p++) & 0xFF;
 
-                 crc = (crc_table[i] ^ (crc << 8)) & 0xFF;
 
-         }
 
 
-         return crc & 0xFF;
 
- }
 
 
- void digitalClockDisplay()
 
- {
 
-   // digital clock display of the time
 
-   // Serial.print(hour());
 
-   // printDigits(minute());
 
-   // printDigits(second());
 
-   // Serial.print(" ");
 
-   // Serial.print(day());
 
-   // Serial.print(".");
 
-   // Serial.print(month());
 
-   // Serial.print(".");
 
-   // Serial.print(year());
 
-   // Serial.println();
 
-   char timeStr[25] = {0};
 
-   sprintf(timeStr, "T:%04d-%02d-%02d %02d:%02d:%02d ", year(), month(), day(), hour(), minute(), second());
 
-   // Serial.println(timeStr);
 
 
-   uint8_t crc = crc8(timeStr, strlen(timeStr));
 
-   sprintf(timeStr, "%s%02X", timeStr, crc);
 
-   Serial.println(timeStr);
 
- }
 
 
- void printDigits(int digits)
 
- {
 
-   // utility for digital clock display: prints preceding colon and leading 0
 
-   Serial.print(":");
 
-   if (digits < 10)
 
-     Serial.print('0');
 
-   Serial.print(digits);
 
- }
 
 
- /*-------- NTP code ----------*/
 
 
- const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
 
- byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
 
 
- time_t getNtpTime()
 
- {
 
-   IPAddress ntpServerIP; // NTP server's ip address
 
 
-   while (Udp.parsePacket() > 0) ; // discard any previously received packets
 
-   Serial.println("I:Transmit NTP Request");
 
-   // get a random server from the pool
 
-   WiFi.hostByName(ntpServerName, ntpServerIP);
 
-   Serial.print("I:");
 
-   Serial.print(ntpServerName);
 
-   Serial.print(": ");
 
-   Serial.println(ntpServerIP);
 
-   sendNTPpacket(ntpServerIP);
 
-   uint32_t beginWait = millis();
 
-   while (millis() - beginWait < 1500) {
 
-     int size = Udp.parsePacket();
 
-     if (size >= NTP_PACKET_SIZE) {
 
-       Serial.println("I:Receive NTP Response");
 
-       Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
 
-       unsigned long secsSince1900;
 
-       // convert four bytes starting at location 40 to a long integer
 
-       secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
 
-       secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
 
-       secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
 
-       secsSince1900 |= (unsigned long)packetBuffer[43];
 
-       return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
 
-     }
 
-   }
 
-   Serial.println("E:No NTP Response :-(");
 
-   return 0; // return 0 if unable to get the time
 
- }
 
 
- // send an NTP request to the time server at the given address
 
- void sendNTPpacket(IPAddress &address)
 
- {
 
-   // set all bytes in the buffer to 0
 
-   memset(packetBuffer, 0, NTP_PACKET_SIZE);
 
-   // Initialize values needed to form NTP request
 
-   // (see URL above for details on the packets)
 
-   packetBuffer[0] = 0b11100011;   // LI, Version, Mode
 
-   packetBuffer[1] = 0;     // Stratum, or type of clock
 
-   packetBuffer[2] = 6;     // Polling Interval
 
-   packetBuffer[3] = 0xEC;  // Peer Clock Precision
 
-   // 8 bytes of zero for Root Delay & Root Dispersion
 
-   packetBuffer[12] = 49;
 
-   packetBuffer[13] = 0x4E;
 
-   packetBuffer[14] = 49;
 
-   packetBuffer[15] = 52;
 
-   // all NTP fields have been given values, now
 
-   // you can send a packet requesting a timestamp:
 
-   Udp.beginPacket(address, 123); //NTP requests are to port 123
 
-   Udp.write(packetBuffer, NTP_PACKET_SIZE);
 
-   Udp.endPacket();
 
- }
 
该部分的代码,参考了Arduino IDE的8266例子中的:
 
在此基础上,进行了一些小的修改:
- 将WiFi连接部分,修改为了WiFiMul,这样就可以同时设置多组WiFi连接,更方便使用。
- 添加了查表计算CRC8的代码,以便接收端收到后进行校验,确保数据准确。不过,MM32L0136C7P不分,我没有使用到,大家有需要可以在接收后进行校验。
- 修改了信息的输出,正常信息使用I:开头,错误信息使用E:开头,时间信息使用T:开头,方便接收端进行解析
 
将以上代码烧录到ESP8266,然后打开串口终端,就可以收到启动的信息,以及时间信息:
 
测试验证完成,就可以连到MM32L0136C7P开发板上使用了。
2. MM32L0136C7P开发板
该部分的代码,参考了SLCD_ShowOnStop、UART_Tx_DMA_Interrupt_Rx_Interrupt。
UART_Tx_DMA_Interrupt_Rx_Interrupt实例,提供了中断收发数据的演示,基本不用修改,连引脚定义都是对应的,直接就可以使用了。
SLCD_ShowOnStop,则提供了显示数字和单位符号的演示,其中关键的调用如下:
- LCD_Clear():清屏
- LCD_DisplayNumber1(索引, '字符', 小数点标志):大号数字区域显示
- LCD_DisplayNumber2(索引, '字符', 小数点标志):小号数字区域显示
- LCD_DisplayUnit(索引, 显示标志):单位符号显示
 
大号数字显示:LCD_DisplayNumber1(索引, '字符', 小数点标志)
索引对应如下:
 
字符对应如下:
 
从上表中可知,除了0-9,还能显示一部分字母,但是不全,例如没有K,因为没法显示区分。
小数点标志,就表示是否显示小数点,0-不显示,1-显示
小号数字显示:LCD_DisplayNumber2(索引, '字符', 小数点标志)
索引对应如下:
 
小号区域的显示字符,演示代码只有0-9。当然,可以根据自己的需要进行扩展。
小数点标志和大号区域相同。
单位符号显示:LCD_DisplayUnit(索引, 显示标志)
索引有10个,对应下图:
{"S1 ", "S2 ", "S3 ", "S4 ", "S9 ", "T1 ", "W1 ", "C1 ", "C2 ", "C3 "};
 
时钟部分,要显示的:,使用的就是C2、C3,索引为8、9
最终,main.c的代码如下:
- ////////////////////////////////////////////////////////////////////////////////
 
- /// [url=home.php?mod=space&uid=288409]@file[/url]    main.c
 
- /// [url=home.php?mod=space&uid=187600]@author[/url]  AE TEAM
 
- /// [url=home.php?mod=space&uid=247401]@brief[/url]   THIS FILE PROVIDES ALL THE SYSTEM FUNCTIONS.
 
- ////////////////////////////////////////////////////////////////////////////////
 
 
- // Define to prevent recursive inclusion
 
- #define _MAIN_C_
 
 
- // Files includes
 
 
- #include "delay.h"
 
- #include "slcd.h"
 
- #include "led.h"
 
- #include "uart_txdma_rx_interrupt.h"
 
 
- ////////////////////////////////////////////////////////////////////////////////
 
- /// @addtogroup MM32_Example_Layer
 
- /// @{
 
 
- ////////////////////////////////////////////////////////////////////////////////
 
- /// @addtogroup MAIN
 
- /// @{
 
 
- ////////////////////////////////////////////////////////////////////////////////
 
- /// @addtogroup MAIN_Exported_Constants
 
- /// @{
 
- #define        GET_BIT(x, bit)        ((x & (1 << bit)) >> bit)        /* 获取第bit位 */
 
 
- #define APP_TITLE "I:MM32 NTP Net Clock\r\n"
 
 
- u8 gSendBuf[] = "I:yyyy-mm-dd HH:MM:SS\r\n";
 
 
- ////////////////////////////////////////////////////////////////////////////////
 
- /// [url=home.php?mod=space&uid=247401]@brief[/url]  This function is main entrance.
 
- /// @param  None.
 
- /// @retval  0.
 
- ////////////////////////////////////////////////////////////////////////////////
 
- s32 main(void)
 
- {
 
-     u8 len;
 
-     u16 times = 0;
 
-     
 
-     u16 year;
 
-     u8 month,day,hour,minute,second;
 
-     u8 crc;
 
-     u8 buf[25] = {0};
 
-     
 
-     DELAY_Init();
 
-     LED_Init();
 
-     DELAY_Ms(100);
 
-     //slcd_test();
 
-     slcd_init();
 
 
-     
 
-     UART1_NVIC_Init(115200);
 
-     NVIC_Configure(DMA1_Channel2_3_IRQn, 2, 0);
 
 
-     UART1_SendString(APP_TITLE);
 
-     DMA_NVIC_Send_Config(DMA1_Channel2, (u32)&UART1->TDR, (u32)gSendBuf, sizeof(gSendBuf)/sizeof(gSendBuf[0]));
 
-     
 
-     LCD_Clear();
 
-     
 
-     LCD_DisplayNumber2(0, '2', 0);
 
-     LCD_DisplayNumber2(1, '0', 0);
 
-     LCD_DisplayNumber2(2, '2', 0);
 
-     LCD_DisplayNumber2(3, '2', 0);
 
 
 
-     LCD_DisplayNumber1(0, '-', 0);
 
-     LCD_DisplayNumber1(1, 'C', 0);
 
-     LCD_DisplayNumber1(2, 'l', 0);
 
-     LCD_DisplayNumber1(3, 'o', 0);
 
-     LCD_DisplayNumber1(4, 'c', 0);
 
-     LCD_DisplayNumber1(5, 'L', 0);
 
-     
 
-     DELAY_Ms(5000);
 
-                 
 
-     while(1) {
 
-         if(gUartRxSta & 0x8000) {
 
-             //receive data length
 
-             len = gUartRxSta & 0x3fff;
 
-             if(len==0x1a && gUartRxBuf[0]=='T' && gUartRxBuf[1]==':') {
 
-                 sscanf(gUartRxBuf, "T:%04d-%02d-%02d %02d:%02d:%02d %2X", &year, &month, &day, &hour, &minute, &second, &crc);
 
-                 sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d\r\n", year, month, day, hour, minute, second);
 
-                 UART1_SendString(buf);
 
-                 LCD_DisplayNumber2(0, ((month/10)%10)?('0'+((month/10)%10)):' ', 0);
 
-                 LCD_DisplayNumber2(1, '0'+((month/1)%10), 0);
 
-                 LCD_DisplayNumber2(2, ((day/10)%10)?('0'+((day/10)%10)):'-', 0);
 
-                 LCD_DisplayNumber2(3, '0'+((day/1)%10), 0);
 
-                 
 
-                 LCD_DisplayNumber1(0, ((hour/10)%10)?('0'+((hour/10)%10)):' ', 0);
 
-                 LCD_DisplayNumber1(1, '0'+((hour/1)%10), 0);
 
-                 LCD_DisplayNumber1(2, '0'+((minute/10)%10), 0);
 
-                 LCD_DisplayNumber1(3, '0'+((minute/1)%10), 0);
 
-                 LCD_DisplayNumber1(4, '0'+((second/10)%10), 0);
 
-                 LCD_DisplayNumber1(5, '0'+((second/1)%10), 0);
 
-                 
 
-                 LCD_DisplayUnit(8,1);
 
-                 LCD_DisplayUnit(9,1);
 
-                 
 
-                 LED1_OFF();
 
-                 LED2_OFF();
 
-                 LED3_OFF();
 
-                 LED4_OFF();
 
-                 if(GET_BIT(second, 0)) {
 
-                     LED1_ON();
 
-                 }
 
-                 if(GET_BIT(second, 1)) {
 
-                     LED2_ON();
 
-                 }
 
-                 if(GET_BIT(second, 2)) {
 
-                     LED3_ON();
 
-                 }
 
-                 if(GET_BIT(second, 3)) {
 
-                     LED4_ON();
 
-                 }
 
-             } else {
 
-                 DMA_NVIC_Send_Config(DMA1_Channel2, (u32)&UART1->TDR, (u32)gUartRxBuf, len);
 
-             }
 
-             gUartRxSta = 0;
 
-         }
 
-         else {
 
-             times++;
 
-             if(times % 5000 == 0) {
 
-                 UART1_SendString(APP_TITLE);
 
-             }
 
 
-             if(times % 500 == 0) UART1_SendString("I:Please input Data, End with Enter\r\n");
 
-             if(times % 50 == 0){
 
-                 LED1_TOGGLE();
 
-                 LED2_TOGGLE();
 
-                 LED3_TOGGLE();
 
-                 LED4_TOGGLE();
 
-             }
 
-             DELAY_Ms(100);
 
-         }
 
-     }
 
 
- }
 
 
- /// @}
 
 
- /// @}
 
 
- /// @}
 
其中的逻辑也不复杂,具体如下:
- 初始化LED、SLCD、串口以及中断和DMA设置
- LCD清屏,然后显示”2022 - ClocL“,没有K,只好用大L代替了
- 然后就是检测接收状态了,收到了,且符合时间字符串的格式,则解析其中的年月日时分秒,然后显示到SLCD,并根据秒来确定对应的LED显示,实际上就是对应1、2、4、8
 
另外,还有下面两个地方的代码,需要修改:
 
 
修改其中LED对应引脚的配置,以对应MM32L0136C7P开发板。
五、实际效果
将以上MM32L0136C7P开发板的代码,使用Keil编译下载后,就可以看到具体效果了:
【以下播放,加快了速度】
 
六、代码分享
1. 官方演示代码:
 MM32L0130_LibSamples_V020_1201.zip
(5.49 MB, 下载次数: 5)
MM32L0130_LibSamples_V020_1201.zip
(5.49 MB, 下载次数: 5)
2. 本应用代码:https://gitee.com/honestqiao/MM32_SLCD_NTP_Net_CLock
要使用本应用的代码,请先下载官方演示代码,并解压,然后将 MM32_SLCD_NTP_Net_CLock 目录放置到 MM32L0130_LibSamples_V020/MM32L0130_Samples/SLCD下,具体如下:
 
红色框柱的为关键代码,如果编译的时候提示找不到对应的文件,请自己添加一下到工程中。