Урок 43
LAN. ENC28J60. Подключаем внешние прерывания (EXINT)
На прошлом уроке мы разобрались, как можно задействовать и обрабатывать внешние прерывания на микроконтроллерах AVR, причём сделали мы это в основном для того, чтобы данный накопленный опыт использовать для программирования нашего сетевого модуля, так как микросхема ENC28J60, являющаяся его сердцем, умеет выдавать определённый сигнал в момент, когда буфер для чтения заполнится. Это позволит нам исключить ошибки, возникающие из-за обработки пришедших пакетов в бесконечном цикле, а также исключить задержки, так как мы уже будем данные пакеты обрабатывать незамедлительно после того, когда они у нас появятся в буфере.
Работаем мы также с Arduino UNO, используя её в качестве отладочной платы.
Поэтому подключим к ней модуль LAN, распиновка приблизительно та же как и с маленькой отладочной платой (нажмите на картинку для увеличения изображения)
Для этого мы первым делом с контакта модуля INT подключим на ножку микроконтроллера PD2 (D2 на Arduino)
Создадим новый проект и назовём его 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-запрос в терминальную программу
Давайте ещё оптимизируем таким же образом запрос с ответом 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));
}
Соберём код, прошьём контроллер и посмотрим результат в терминальной программе после посылки пинга нашему модулю
Иногда также появляются пакеты типа 6, скорей всего это пакеты протокола IP версии 6
Также результаты нашей оптимизации отлично просматриваются в скорости пингов
В данном занятии мы решили сразу 2 задачи — использование внешних прерываний для своевременного принятия и обработки пакетов от сетевого модуля, а также оптимизация кода засчёт исключения лишних посылок в шину USART.
Предыдущий урок Программирование МК AVR Следующий урок
Приобрести плату Arduino UNO R3 можно здесь.
Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий