AVR Урок 43. LAN. ENC28J60. Подключаем внешние прерывания (EXINT)



 

Урок 43

 

LAN. ENC28J60.  Подключаем внешние прерывания (EXINT)

 

На прошлом уроке мы разобрались, как можно задействовать и обрабатывать внешние прерывания на микроконтроллерах AVR, причём сделали мы это в основном для того, чтобы данный накопленный опыт использовать для программирования нашего сетевого модуля, так как микросхема ENC28J60, являющаяся  его сердцем, умеет выдавать определённый сигнал в момент, когда буфер для чтения заполнится. Это позволит нам исключить ошибки, возникающие из-за обработки пришедших пакетов в бесконечном цикле, а также исключить задержки, так как мы уже будем данные пакеты обрабатывать незамедлительно после того, когда они у нас появятся в буфере.

Работаем мы также с Arduino UNO, используя её в качестве отладочной платы.

Поэтому подключим к ней модуль LAN, распиновка приблизительно та же как и с маленькой отладочной платой (нажмите на картинку для увеличения изображения)

 

image05_0500

 

Для этого мы первым делом с контакта модуля INT подключим на ножку микроконтроллера PD2 (D2 на Arduino)

 

image00

 

Создадим новый проект и назовём его ENC28J60_INT. Все файлы с исходными кодами, кроме main.c, мы скопируем из проекта ENC28J60_ARP и подключим их проект, а код из файла main.c прошлого проекта мы скопируем в одноимённый файл нового проекта, перед этим полностью очистив его от нового текста.

В файле main.c добавим функцию инициализации портов и включим ножку порта PD2 на вход. Это наша ножка, которая будет принимать внешние прерывания

 

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

void port_ini(void)

{

//Включим ножку INT0(PD2) на вход

DDRD &= ~(0b00000100);

}

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

 

Также добавим ещё одну функцию для инициализации регистров, отвечающих за внешние прерывания

 

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

void int_ini(void)

{

//включим прерывания INT0 по нисходящему фронту

EICRA |= (1<<ISC01);

//разрешим внешнее прерывание INT0

EIMSK |= (1<<INT0);

}

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

 

 

Теперь вызовем наши функции в main()

 

init_timer();

port_ini();

int_ini();

USART_Init (16); //115200

 

Закомментируем функцию вызова обработки сети в бесконечном цикле

 

while (1)

{

  //net_pool();

}

 

Перейдём в файл net.c и добавим в самом низу файла функцию обработки внешних прерываний, в которой и вызовем функцию обработки сети

 

//—————————————————

ISR(INT0_vect)

{

  net_pool();

}

//—————————————————

 

Соберём код и прошьём контроллер, у нас по-прежнему должно всё работать, только работать код будет более уверенно, так как пакеты обрабатываются теперь вовремя.

Давайте теперь немного оптимизируем наш код.

Перейдём в файл enc28j60.c и уберём задержку из функции enc28j60_packetSend

 

  enc28j60_writeOp(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS);

  _delay_ms(1);

}

 

Также у нас очень много времени тратится на отправку байтов в USART, причём делаем мы это ещё до отправки ответных пакетов, то есть не вовремя, тем самым задерживая процесс обработки пакета.

 

 

Начнём с обработки пакетов ARP REQUEST.

Для вернёмся в файл net.c в функцию eth_read и сначала закомментируем там всё, связанное с USART

 

if (len>=sizeof(enc28j60_frame_ptr))

{

  // sprintf(str1,"%02X:%02X:%02X:%02X:%02X:%02X-%02X:%02X:%02X:%02X:%02X:%02X; %d; %04X",

  // frame->addr_src[0],frame->addr_src[1],frame->addr_src[2],frame->addr_src[3],frame->addr_src[4],frame->addr_src[5],

  // frame->addr_dest[0],frame->addr_dest[1],frame->addr_dest[2],frame->addr_dest[3],frame->addr_dest[4],frame->addr_dest[5],

  // len, be16toword(frame->type));

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

  if(frame->type==ETH_ARP)

  {

    // USART_TX((uint8_t*)"; arp",5);

    res=arp_read(frame,len-sizeof(enc28j60_frame_ptr));

    if(res==1)

    {

      arp_send(frame);

    }

    else if(res==2)

    {

      arp_table_fill(frame);

    }

  }

  else if(frame->type==ETH_IP)

  {

    //USART_TX((uint8_t*)"; ip",4);

    ip_read(frame,len-sizeof(ip_pkt_ptr));

  }

  //USART_TX((uint8_t*)"\r\n",2);

}

 

Также если вдруг прийдёт какой-нибудь залётный пакет, не относящийся ни к ARP, ни к IP, то для него сделаем отдельную обработку

 

  else if(frame->type==ETH_IP)

  {

    //USART_TX((uint8_t*)"; ip",4);

    ip_read(frame,len-sizeof(ip_pkt_ptr));

  }

  else

  {

    sprintf(str1,"%02X:%02X:%02X:%02X:%02X:%02X-%02X:%02X:%02X:%02X:%02X:%02X; %d; %04X",

    frame->addr_src[0],frame->addr_src[1],frame->addr_src[2],frame->addr_src[3],frame->addr_src[4],frame->addr_src[5],

    frame->addr_dest[0],frame->addr_dest[1],frame->addr_dest[2],frame->addr_dest[3],frame->addr_dest[4],frame->addr_dest[5],

    len, be16toword(frame->type));

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

    USART_TX((uint8_t*)"\r\n",2);

  }

  //USART_TX((uint8_t*)"\r\n",2);

}

 

Идём теперь в файл arp.c в функцию arp_read и также уберём в ветке условия ARP_REQUEST всё связанное с USART

 

if (msg->op==ARP_REQUEST)

{

  // sprintf(str1,"request\r\nmac_src %02X:%02X:%02X:%02X:%02X:%02X\r\n",

  // msg->macaddr_src[0],msg->macaddr_src[1],msg->macaddr_src[2],msg->macaddr_src[3],msg->macaddr_src[4],msg->macaddr_src[5]);

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

  // sprintf(str1,"ip_src %ld.%ld.%ld.%ld\r\n",

  // msg->ipaddr_src & 0x000000FF,(msg->ipaddr_src>>8) & 0x000000FF,

  // (msg->ipaddr_src>>16) & 0x000000FF, msg->ipaddr_src>>24);

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

  // sprintf(str1,"mac_dst %02X:%02X:%02X:%02X:%02X:%02X\r\n",

  // msg->macaddr_dst[0],msg->macaddr_dst[1],msg->macaddr_dst[2],msg->macaddr_dst[3],msg->macaddr_dst[4],msg->macaddr_dst[5]);

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

  // sprintf(str1,"ip_dst %ld.%ld.%ld.%ld\r\n",

  // msg->ipaddr_dst & 0x000000FF,(msg->ipaddr_dst>>8) & 0x000000FF,

  // (msg->ipaddr_dst>>16) & 0x000000FF, msg->ipaddr_dst>>24);

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

  res=1;

}

 

Теперь идём в функцию arp_send и вот здесь после отправки пакета мы всю информацию и покажем в терминале, не забывая про то, что источник с приёмником у нас теперь поменялись местами

 

  eth_send(frame,sizeof(arp_msg_ptr));

  sprintf(str1,«%02X:%02X:%02X:%02X:%02X:%02X(%ld.%ld.%ld.%ld)-«,

    frame->addr_dest[0],frame->addr_dest[1],frame->addr_dest[2],frame->addr_dest[3],frame->addr_dest[4],frame->addr_dest[5],

    msg->ipaddr_dst & 0x000000FF,(msg->ipaddr_dst>>8) & 0x000000FF,

    (msg->ipaddr_dst>>16) & 0x000000FF, msg->ipaddr_dst>>24);

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

  sprintf(str1,«%02X:%02X:%02X:%02X:%02X:%02X(%ld.%ld.%ld.%ld) arp request\r\n»,

    frame->addr_src[0],frame->addr_src[1],frame->addr_src[2],frame->addr_src[3],frame->addr_src[4],frame->addr_src[5],

    msg->ipaddr_src & 0x000000FF,(msg->ipaddr_src>>8) & 0x000000FF,

    (msg->ipaddr_src>>16) & 0x000000FF, msg->ipaddr_src>>24);

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

}

 

Отправим пинг на нашу плату, нам дожен будет прийти хотя бы один ARP-запрос в терминальную программу

 

image01

 

Давайте ещё оптимизируем таким же образом запрос с ответом ICMP.

В функции icmp_read также закомментируем всё лишнее

 

if ((len>=sizeof(icmp_pkt_ptr))&&(icmp_pkt->msg_tp==ICMP_REQ))

{

  // sprintf(str1,"icmp request\r\n");

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

  icmp_pkt->msg_tp=ICMP_REPLY;

 

А всю информацию мы выведем в этой же функции, но только после после отправки пакета (mac-адреса в данном случае нам уже не нужны)

 

  ip_send(frame,len+sizeof(ip_pkt_ptr));

  sprintf(str1,"%ld.%ld.%ld.%ld-%ld.%ld.%ld.%ld icmp request\r\n",

    ip_pkt->ipaddr_dst & 0x000000FF,(ip_pkt->ipaddr_dst>>8) & 0x000000FF,

    (ip_pkt->ipaddr_dst>>16) & 0x000000FF, ip_pkt->ipaddr_dst>>24,

    ip_pkt->ipaddr_src & 0x000000FF,(ip_pkt->ipaddr_src>>8) & 0x000000FF,

    (ip_pkt->ipaddr_src>>16) & 0x000000FF, ip_pkt->ipaddr_src>>24);

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

}

 

Соберём код, прошьём контроллер и посмотрим результат в терминальной программе после посылки пинга нашему модулю

 

image02

 

Иногда также появляются пакеты типа 6, скорей всего это пакеты протокола IP версии 6

 

image04

 

Также результаты нашей оптимизации отлично просматриваются в скорости пингов

 

image03

 

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

 

 

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

 

 

Исходный код

 

 

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

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

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

 

 

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

 

AVR Name

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

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

*