Урок 40
Часть 7
LAN. ENC28J60
В предыдущей части нашего урока мы познакомились с протоколом ARP начали писать функцию чтения пакетов ARP.
Теперь в функции arp_read добавим в тело условия отображение типа запроса, адресов источника и адресов приёмника, и установим результат
if( (msg->op == ARP_REQUEST) && (msg->ipaddr_dst == IP_ADDR) )
{
sprintf(str1,«requestrnmac_src %02X:%02X:%02X:%02X:%02X:%02Xrn»,
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.%ldrn»,
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:%02Xrn»,
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_src %ld.%ld.%ld.%ld»,
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-запрос, мы же ведь никому не говорили о нашем IP-адресе.
А мы ему поможем. Мы воспользуемся утилитой ping, встроенной в любую операционную систему. Хотя ping посылает ICMP-запрос, но если он видит, что в таблице соответствия адресов ARP нет нашего IP-адреса, то сначала он посылает ARP-запрос.
Поэтому мы и пошлём ping на назначенный нами IP-адрес из командной строки, понятное дело, собрав перед этим код и прошив контроллер.
Мы видим, что наша плата на ping не откликнулась, но это ничего страшного, мы же ведь не писали ответ не только на ICMP-запросы, но и на ARP, но зато мы вот что видим в нашей терминальной программе
Мы видим и физический и сетевой адреса устройства, пославшего нам запрос (это наш основной ПК с адресом 192.168.1.89), а также сетевой адрес приёмника. Так как физический адрес приёмника пока неизвестен, то мы получаем нулевой адрес.
Очередная задача, стоящая перед нами — ответить на ARP-запрос.
Но мы помним, что мы написали только функцию приёма пакетов Ethernet (или кадров). А чтобы на что-то ответить, мы должны уметь также такие пакеты отправлять. Поэтому нам придётся вернуться в файл enc28j60.c и добавить функцию для передачи пакетов
//—————————————-
void enc28j60_packetSend(uint8_t *buf, uint16_t buflen)
{
}
//—————————————-
Подождём, пока установится бит TXRTS в регистре ECON1, что означает готовность нашего передатчика передавать информацию
void enc28j60_packetSend(uint8_t *buf, uint16_t buflen)
{
while (enc28j60_readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
{
}
}
Дальше борьба с ошибкой микросхемы, согласно которой бит TXRTS сам не сбрасывается. Мы сначала проверяем регистр EIR на установку бита TXERIF, а затем сбрасываем бит
while (enc28j60_readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
{
if (enc28j60_readRegByte(EIR) & EIR_TXERIF)
{
enc28j60_writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
enc28j60_writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
}
}
Добавим функцию записи в буфер
//—————————————-
static void enc28j60_writeBuf(uint16_t len, const uint8_t* data) {
SS_SELECT();
SPI_SendByte(ENC28J60_WRITE_BUF_MEM);
while (len—)
SPI_SendByte(*data++);
SS_DESELECT();
}
//—————————————-
Продолжаем функцию отправки пакета. Затем записываем и отправляем пакет
enc28j60_writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
}
}
enc28j60_writeReg(EWRPT, TXSTART_INIT);
enc28j60_writeReg(ETXND, TXSTART_INIT+buflen);
enc28j60_writeBuf(1,(uint8_t*)«\x00»);
enc28j60_writeBuf(buflen, buf);
enc28j60_writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}
Добавим прототип данной функции в заголовочный файл и вернёмся в net.c. В фунцкии eth_read немного исправим код, чтобы отправлять ответ ARP только в случае, если модуль узнает свой адрес
USART_TX((uint8_t*)«; arp»,5);
if(arp_read(frame, len — sizeof(enc28j60_frame_ptr)))
{
}
Добавим физический адрес нашего устройства, унаследовав его из другого модуля
uint8_t net_buf[ENC28J60_MAXFRAME];
extern uint8_t macaddr[6];
Добавим функцию для отправки кадра по протоколу Ethernet куда-нибудь повыше, в которой мы установим физические адреса источника и приёмника, а затем отправим пакет
//———————————————————
void eth_send(enc28j60_frame_ptr *frame, uint16_t len)
{
memcpy(frame->addr_dest, frame->addr_src, 6);
memcpy(frame->addr_src, macaddr, 6);
enc28j60_packetSend((void*)frame, len + sizeof(enc28j60_frame_ptr));
}
//———————————————————
Добавим также функцию отправки ответного пакета ARP сразу после функции eth_send
//———————————————————
void arp_send(enc28j60_frame_ptr *frame)
{
}
//———————————————————
В следующей части нашего урока мы наконец-то ответим на запрос ARP, а также изучим заголовок протокола IP.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Приобрести плату Atmega 328p Pro Mini можно здесь.
Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добрый день.
Нет описания значения EIR_TXERIF в текстовой версии,
В функции записи в буфер ENC28J60_WRITE_BUF_MEM undeclared (first use in this function)
В общем проект не собирается…
Нашел в видео-уроке значения EIR_TXERIF, поставил.
с ENC28J60_WRITE_BUF_MEM, так же поставил ENC28J60_READ_BUF_MEM как в видео.
Где истина не уяснил, но пока все работает как у Admina.