AVR Урок 47. LAN. ENC28J60. NTP. Узнаём точное время. Часть 2



 

Урок 47

 

Часть 2

 

LAN. ENC28J60. NTP. Узнаём точное время

 

В предыдующей части нашего урока мы узнали всё необходимое о протоколе NTP и написали код, который отправил запрос, дождался ответа от сервера и отобразил в терминальной программе мировое время в виде таймштампа в секундах.

 

Теперь перед нами стоит задача — время в секундах преобразовать в удобочитаемый формат.

Сначала в файле ntp.h добавим макрос для часового пояса. У нас часовой пояс третий, надеюсь, вы свой знаете

 

#define LOCAL_PORT_FOR_NTP 14444

#define TIMEZONE 3

 

Теперь вернёмся в нашу функцию и напишем код преобразования, благо для этого у нас есть библиотека time.h

 

USART_TX((uint8_t*)str1,strlen(str1));

time_t rawtime = (time_t)(be32todword(ntp_pkt->tx_ts.sec)-NTP_OFFSET+60UL*60*TIMEZONE);

timestruct = localtime(&rawtime);

 

Здесь мы уже используем для временного хранения ещё одну структуру — time_t для установки указателя в соответствующее место пакета NTP. Также сделаем смещение на формат NTP, так как он предусматртивает отсчёт времени от другой даты, нежели стандартный таймштамп, а также смещаемся на наш часовой пояс, умножив его соответственно на кол-во секунд в часе. Затем мы с помощью функции localtime структурируем сырое значение времени в удобочитаемое.

Ну и останется нам всё это показать в терминальной программе

 

  timestruct = localtime(&rawtime);

  sprintf(str1,"%02d.%02d.%04u %02d:%02d:%02d -%d-\r\n", timestruct->tm_mday,timestruct->tm_mon+1, //ћес¤цы считаются от 0, а не от 1

  timestruct->tm_year+1900,timestruct->tm_hour,timestruct->tm_min,timestruct->tm_sec,timestruct->tm_wday);

  USART_TX((uint8_t*)str1,strlen(str1));

  return res;

}

 

К году прибавляется 1900, так как именно с этого момента они считаются с учетом смещения, это указано и в самой библиотеке time.h.

Соберём ещё раз наш код, прошьём контроллер и посмотрим результат, убедившись, что он соответствует реальному времени

 

 image08

 

День недели у нас нулевой, так как сегодня воскресенье, а дни недели считаются в библиотеке от 0 до 6

 

int8_t tm_wday; /**< days since Sunday - [ 0 to 6 ] */

 

При желании можно написать условие и открорректировать это, но таких задач перед нами не стоит.

У нас более серьёзная задача — получить время во что бы то не стало, а оно сразу не приходит, поэтому автоматизируем наши запросы ну или попытки. Это оказалось не так-то легко.

Создадим ради этого ещё одну структуру для статуса нашего клиента NTP в файле ntp.h

 

} ntp_pkt_ptr;

//--------------------------------------------------

typedef struct ntp_prop{

  uint8_t ntp_cnt; //количество оставшихся попыток получить время

  int32_t ntp_timer; //таймер для следующей попытки

  uint8_t set; //флаг получения времени

  uint8_t macaddr_dst[6];

  uint32_t ip_dst;

  uint16_t port_dst;//порт получателя

} ntp_prop_ptr;

//----------------------------------------------

 

В принципе назначение каждого поля я осветил в комментариях.

 

 

Так как попытки будут осуществляться из другого места, не всегда из net_cmd, то решение было принято адреса все хранить в структуре.

В файле ntp.c создадим переменную типа нашей структуры

 

extern uint8_t macaddr[6];

ntp_prop_ptr ntpprop;

 

В файле net.c её мы также подключим

 

extern USART_prop_ptr usartprop;

extern ntp_prop_ptr ntpprop;

 

В функции инициализации инициализируем основные поля нашей структуры на всякий случай

 

enc28j60_ini();

ntpprop.set=0;

ntpprop.ntp_cnt=0;

ntpprop.ntp_timer=0;

 

В одном из условий в функции net_cmd занесём адрес ip в соответствующее поле нашей структуры

 

else if(usartprop.is_ip==6)//статус попытки отправить NTP-пакет

{

  ip=ip_extract((char*)usartprop.usart_buf,usartprop.usart_cnt);

  ntpprop.ip_dst = ip;

 

Также в этой же функции мы добавим указатель на наш пакет

 

static uint16_t port=0;

enc28j60_frame_ptr *frame=(void*)net_buf;

 

 

В следующем условии также занесём значения в соответствующие поля структуры и воспользуемся на всякий случай уже ими в вызове функции отправки запроса NTP

 

else if(usartprop.is_ip==7)//статус отправки NTP-пакета

{

  port=port_extract((char*)usartprop.usart_buf,usartprop.usart_cnt);

  ntpprop.port_dst = port;

  ntpprop.ntp_cnt = 10; //10 попыток

  ntpprop.ntp_timer = 5;//5 секунд до следующей попытки

  ntpprop.set=1;//флаг запроса времени взведен

  memcpy(ntpprop.macaddr_dst,frame->addr_dest,6);

  ntp_request(ntpprop.ip_dst,ntpprop.port_dst);

 

Вообщем здесь мы назначили сделать 10 попыток для запроса NTP, также временной интервал в 5 секунд между попытками и взвели флаг запроса времени, который мы при удачном ответе сервера будем сбрасывать.

Теперь идём в обработчик таймера ISR (TIMER0_COMPA_vect) и добавим там следующее условие, которое определит, установлен ли вообще флаг запроса NTP

 

clock_cnt++;

if (ntpprop.set)

{

}

 

Затем в теле добавленного условия мы убавим секунды до следующей попытки

 

if (ntpprop.set)

{

  ntpprop.ntp_timer--;

}

 

Затем добавим в этом условии ещё одно условие, подтверждающее то, что у нас таймер дошел до нуля, а количество попыток у нас ещё не закончилось

 

ntpprop.ntp_timer--;

if ((ntpprop.ntp_timer<0)&&(ntpprop.ntp_cnt>0))

{

}

 

А в теле этого условия мы опять установим время до следующей попытки, убавим количество попыток и отправим запрос NTP серверу, перед этим отобразив в терминальной программе количество оставшихся попыток

 

if ((ntpprop.ntp_timer<0)&&(ntpprop.ntp_cnt>0))

{

  ntpprop.ntp_timer = 5;

  ntpprop.ntp_cnt--;

  sprintf(str1,"ntp_cnt: %d\r\n",ntpprop.ntp_cnt);

  USART_TX((uint8_t*)str1,strlen(str1));

  ntp_request(ntpprop.ip_dst,ntpprop.port_dst);

}

 

Выйдем только из этого условия и добавим следующее, в котором сбросим все флаги

 

  ntp_request(ntpprop.ip_dst,ntpprop.port_dst);

}

else if (ntpprop.ntp_cnt<=0)

{

  //сбросим все флаги и счетчики

  ntpprop.set=0;

  ntpprop.ntp_cnt=0;

  ntpprop.ntp_timer=0;

}

 

Перейдём в функцию отправки запроса NTP ntp_request в файл ntp.c и занесём в соответствующее поле MAC-адрес приёмника, так как он вполне может исказиться, если между попытками произойдёт приём или отправка других пакетов, соответственно наш буфер может быть перезаполнен, а в данной функции мы этот адрес нигде явно не заносим

 

memcpy(frame->addr_src,macaddr,6);

memcpy(frame->addr_dest,ntpprop.macaddr_dst,6);

frame->type=ETH_IP;

 

Ну и также в функции приёма NTP-пакета от сервера ntp_read мы также сбросим флаги нашей структуры на случай, если пакет придёт не с последней попытки, чтобы нам больше запросы в случае удачного ответа больше не посылать

 

  USART_TX((uint8_t*)str1,strlen(str1));

  //сбросим все флаги и счетчики

  ntpprop.set=0;

  ntpprop.ntp_cnt=0;

  ntpprop.ntp_timer=0;

  return res;

}

 

Соберём код. прошьём контроллер и посмотрим результат нашего труда

 

image09

 

Ну вот, сервер вернул время с 5 попытки, обычно где-то со 2 или 3-й. С 1-й очень редко.

Также я пробовал запустить бесконечные пинги на наш модуль с компьютера и в это время запросить также точное время. Сервер его возвращал. Поэтому в таком случае также всё у нас работает.

Вы можете устанавливать свои величины времени до следующей попытки, также своё количество попыток.

Самое главное, чтобы вы поняли сам смысл работы протоколов и расчётов в них, что, я надеюсь вам дал. Конечно, не обязательно, всё делать именно таким образом. Вы можете придумать свой способ запроса времени. Тем не менее, надеюсь, что мой урок вам пойдёт на пользу и свою мысль или своё видение данного вопроса мне передать вам удалось.

Когда-нибудь, вооружившись знаниями из этого урока, мы, возможно напишем какие-нибудь часы с синхронизацией из интернет.

Спасибо за внимание!

 

 

Предыдущая часть Программирование МК AVR Следующий урок

 

Исходный код

 

 

Приобрести плату Arduino UNO R3 можно здесь.

Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером

Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)

AVR LAN. ENC28J60. NTP. Узнаём точное время

 

Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)

AVR LAN. ENC28J60. NTP. Узнаём точное время

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*