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 2.0

Ethernet LAN Сетевой Модуль можно купить здесь (модуль SD SPI в подарок) ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

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

 

AVR Name

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

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

*