Программирование ARM STM32F407 и сеть Ethernet на ENC28J60 Fri, December 13 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


STM32F407 и сеть Ethernet на ENC28J60 Печать
Добавил(а) microsin   

Недавно попробовал сделать сетевое подключение к микроконтроллеру STM32 на основе недорогого модуля с чипом ENC28J60. Такие модули стоят в Интернет-магазинах от 3.5 до 6 долларов [1, 2].

ENC28J60-module-top-IMG 2657 ENC28J60-module-bottom-IMG 2665

Модуль подключил к плате Olimex STM32-P407 [6] через коннектор UEXT.

ENC28J60-connected-STM32-P407-IMG 2651

Кабель, который соединяет коннектор модуля ENC28J60 и коннектор UEXT:

UEXT STM32-P407                      Модуль ENC28J60
1  +3.3V             черный          9  VCC
2  GND               белый           10 GND
3                    серый    
4                  фиолетовый 
5  PG10              синий           2  INT
6  PG12             зеленый          8  RST
7  PC11              желтый          4  S0
8  PC12            оранжевый         5  SI
9  PC10             красный          6  SCK
10 PF8             коричневый        7  CS

Реально в программе для управления чипом ENC28J60 используются пока только сигналы RST, SO, SI, SCK и CS.

Проекты, которые можно найти в интернете для STM32, в основном основаны на FreeRTOS или CooCox CoOS. Проект, который рассматривается здесь, был сделан для STM32F407 на основе простейшего проекта из IAR (демонстрация работы с GPIO, оттуда взят код инициализации ядра) и исходного кода uIP [3], без использования RTOS (обычный простой бесконечный цикл main). Подпрограммы для инициализации ENC28J60 были взяты из репозитория coocox.org [4], и доработаны, чтобы они работали на SPI3 платы Olimex STM32-P407. Полностью готовый проект для IAR 6.5 можно скачать по ссылке [5].

[Описание кода для работы с ENC28J60]

В библиотеке uIP [3] есть несколько отличных примеров для реализации готовых сервисов. Достаточно в файле конфигурации uip-master\unix\uip-conf.h раскомментировать нужную строчку, и соответствующий сервис начнет работать (в этом примере раскомментирован демонстрационный сервис hello-world, который выдает строку диалога при подключении клиентом telnet к порту 1000):

...
/* Здесь мы подключаем заголовочный файл для нужного сервиса
 (или сервисов), которые будут использоваться в приложении. */
/*#include "smtp.h"*/
#include "hello-world.h"
/*#include "telnetd.h"*/
/*#include "webserver.h"*/
/*#include "dhcpc.h"*/
/*#include "resolv.h"*/
/*#include "webclient.h"*/
...

Для начала инициализации подсистемы сети нужно проинициализировать чип ENC28J60 и настроить его на нужный адрес, шлюз и маску подсети. В этом примере сетевой адаптер на ENC28J60 получает MAC адрес 00-12-34-56-78-00, IP адрес 192.168.0.57, IP адрес шлюза 192.168.0.1 и маску подсети 255.255.255.0:

...
struct uip_eth_addr mac = { { 0x00, 0x12, 0x34, 0x56, 0x78, 0x00 } };
uip_ipaddr_t ipaddr;
enc28j60_init(mac.addr);
uip_init();
uip_arp_init();
hello_world_init();
uip_setethaddr(mac);
uip_ipaddr(ipaddr, 192, 168, 0, 57);
uip_sethostaddr(ipaddr);
uip_ipaddr(ipaddr, 192, 168, 0, 1);
uip_setdraddr(ipaddr);
uip_ipaddr(ipaddr, 255, 255, 255, 0);
uip_setnetmask(ipaddr);
...

Далее, чтобы система отвечала на ping и обрабатывала нужные сервисы (в нашем примере это сервис hello-world), нужно в бесконечном цикле вызывать подпрограмму vTask_uIP (в проектах, которые используют FreeRTOS или другие операционные системы реального времени, эта подпрограмма оформлена в виде отдельной задачи).

void main()
{
   //Тут должен быть вставлен код инициализации.
   ...
   while(1)
   {
      vTask_uIP();
   }
}

[Как это работает]

Запустите интерпретатор cmd, и попробуйте ввести команды ping 192.168.0.57, arp -a, telnet 192.168.0.57 1000. Получите примерно следующее:

C:\Documents and Settings\user>ping 192.168.0.57
  
Обмен пакетами с 192.168.0.57 по 32 байт:
  
Ответ от 192.168.0.57: число байт=32 время< 1мс TTL=128
Ответ от 192.168.0.57: число байт=32 время< 1мс TTL=128
Ответ от 192.168.0.57: число байт=32 время< 1мс TTL=128
Ответ от 192.168.0.57: число байт=32 время< 1мс TTL=128
  
Статистика Ping для 192.168.0.57:
    Пакетов: отправлено = 4, получено = 4, потеряно = 0 (0% потерь),
Приблизительное время приема-передачи в мс:
    Минимальное = 0мсек, Максимальное = 0 мсек, Среднее = 0 мсек
  
C:\Documents and Settings\user>arp -a
  
Интерфейс: 192.168.0.56 --- 0x3
  Адрес IP              Физический адрес      Тип
  192.168.0.1           1c-7e-e5-e5-99-60     динамический
  192.168.0.14          c4-85-08-b6-7e-8e     динамический
  192.168.0.57          00-12-34-56-78-00     динамический
  
C:\Documents and Settings\user>telnet 192.168.0.57 1000
Hello. What is your name?
                         microsin
Hello microsin
  
C:\Documents and Settings\user>

[Устранение возможных проблем]

Q001. Почему код не работает, устройство не отвечает на ping, сервис hello-word не отображает диалог?

Нужно обратить внимание на следующее:

1. Проверьте правильность подключения чипа ENC28J60, лучше всего это сделать с помощью осциллографа. На разъеме платы должно быть надежное питание +3.3V. Когда проект запущен и работает, на ENC28J60 должны присутствовать все сигналы RST (в момент первого запуска должен пройти одиночный импульс лог. 0), SI, SO, SCK, CS.
2. Проверьте кабель Ethernet. Когда Вы подключаете к модулю ENC28J60 кабель, то должен на коннекторе RJ45 загореться зеленый светодиод, сигнализирующий о правильном физическом подключении.
3. Попробуйте вставить и подобрать задержки в программе перед переводом сигнала CS в состояние лог. 1 (т. е. задержку надо попробовать вставить перед вызовами макросов enc28j60_release). Если в проекте включена оптимизация по скорости (например High, Balanced), то попробуйте также подобрать задержку после перевода сигнала CS в 0 (т. е. задержку надо попробовать вставить сразу после вызовов макросов enc28j60_select).
4. Попробуйте подобрать скорость SPI, поменяв её в сторону уменьшения и увеличения.
5. Проверьте настройки сети, которые Вы записываете в ENC28J60 (IP, mask, gateway) - они должны соответствовать настройкам сети, куда Вы подключаете модуль ENC28J60.

Лично у меня была проблема в п. 3 - мне как раз пришлось подбирать задержки в функциях enc28j60_write_op, enc28j60_write_buffer. Также мне помог п. 4 - после того, как я увеличил скорость SPI3 до максимума, задержки оказались не нужны, они были заменены циклами проверки флага BSY SPI3 (см. описание регистров и флагов SPI в [7]). Вот эта доработка (цикл while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_BSY));):

// Обычная запись команды через SPI
void enc28j60_write_op(uint8_t cmd, uint8_t adr, uint8_t data)
{
   enc28j60_select();
   enc28j60_tx(cmd | (adr & ENC28J60_ADDR_MASK));
   enc28j60_tx(data);
   while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_BSY));
   enc28j60_release();
}
// Запись буфера Rx/Tx (в момент EWRPT)
void enc28j60_write_buffer(uint8_t *buf, uint16_t len)
{
   enc28j60_select();
   enc28j60_tx(ENC28J60_SPI_WBM);
   while(len--)
      enc28j60_tx(*(buf++));
   while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_BSY));
   enc28j60_release();
}

Q002. Почему после того, как я поменял MAC-адрес, перекомпилировал и перезапустил проект, мое устройство ENC28J60 перестало отвечать на ping, и его больше не видно в сети?

Дело в том, что операционная система, на которой Вы запускаете команду ping (например Windows), делает кэширование MAC-адресов. Т. е. она составляет таблицу соответствия MAC:IP, которая используется для ускорения доступа к хостам сети. Посмотреть текущую таблицу на операционной системе Windows можно командой arp -a:

C:\Documents and Settings\user>arp -a
  
Интерфейс: 192.168.0.56 --- 0x3
  Адрес IP              Физический адрес      Тип
  192.168.0.1           1c-7e-e5-e5-99-60     динамический
  192.168.0.14          c4-85-08-b6-7e-8e     динамический
  192.168.0.57          00-12-34-56-78-00     динамический

Записи в таблицу попадают с помощью выдачи специального запроса по протоколу ARP, и если IP-адрес уже попал в эту таблицу, то больше не выполняется процедура arp-запроса. Поэтому после того, как Вы поменяли MAC, Windows об этом ничего не узнает, и будет пытаться обращаться по IP к старому MAC-адресу. Для того, чтобы решить проблему, достаточно очистить таблицу MAC-адресов командой arp -d. После этого по протоколу ARP будет выдан новый запрос, и запись в таблицу MAC-адресов уже попадет с новым, правильным MAC-адресом, и сетевое соединение снова заработает, устройство на ENC28J60 снова начнет отвечать на ping.

Q003. Почему пример сетевого приложения hello-world.c после установки сетевого соединения принимает максимум 8 символов имени, а если ввести больше, то начинаются глюки?

Это потому, что в приложении hello-world зарегистрирован входной буфер для ввода символов размером в 10 байт, и если ввести больше 8 символов, то вместе с символами возврата каретки и перевода строки получится больше 10 символов. Входной буфер задается как поле inputbuffer[10] при определении структуры uip_tcp_appstate_t в файле hello-world.h:

typedef struct hello_world_state {
  struct psock p;
  char inputbuffer[10];
  char name[40];
} uip_tcp_appstate_t;

Затем этот буфер инициализируется как входной при инициализации сокета в тот момент, когда создается соединение:

/*---------------------------------------------------------------------------
 * В файле hello-world.h есть определение макроса UIP_APPCALL для
 * hello_world_appcall, так что эта функция является функцией приложения uIP.
 * Эта функция будет вызвана при любом событии uIP (например, установлено
 * новое соединение, пришли новые данные, отправленные данные были 
 * подтверждены, данные нужно передать повторно и т. д.).
 */
void hello_world_appcall(void)
{
  /*
 * Структура uip_conn содержит поле, называемое "appstate", которое
 * хранит состояние соединения приложения. Для упрощения доступа мы здесь
 * создали указатель.
 */
   struct hello_world_state *s = &(uip_conn->appstate);
  /*
 * Если новое соединение только что было установлено, то мы должны
 * инициализировать protosocket в структуре состояния приложения.
 */
   if(uip_connected())
   {
      PSOCK_INIT(&s->p, s->inputbuffer, sizeof(s->inputbuffer));
   }
  /*
 * На последнем шаге мы запускаем protosocket-функцию, которая в действительности
 * поддерживает обмен данными. Мы передали указатель на структуру состояния
 * приложения для текущего соединения.
 */
   handle_connection(s);
}

[Ссылки]

1. ENC28J60 Ethernet LAN / Network Module site:dx.com.
2. Mini ENC28J60 Ethernet LAN / Network Module site:ebay.com.
3. adamdunkels/uip site:github.com.
4. enc28j60 site:coocox.org.
5. 140825STM32-ethernet.zip - проект для IAR 6.50.
6. Olimex STM32-P407.
7. STM32F407, интерфейс SPI.
8. DP83848 Ethernet Board site:emartee.com

 

Комментарии  

 
+1 #1 Иван 25.07.2016 15:50
Переписал ваш проект для платы STM32F4 discovery, убрал задержку delay.c и заработало считывание с регистра микросхемы ENC28J60. А подскажите пожалуйста, как организовать связь через роутер с включенным DHCP? В проекте указал адрес хоста, но на пинг модуль не отвечает.

microsin: чтобы работал ping код, работающий на STM32F4, должен поддерживать протокол ICMP. И конечно, должен быть правильно настроен адрес IP в проекте, либо должна быть реализована поддержка протокола DHCP, если хотите получать адрес IP автоматически.
Цитировать
 

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


Защитный код
Обновить

Top of Page