STM Урок 91. LAN. W5500. HTTP Server. Часть 1



 

Урок 91

 

Часть 1

 

LAN. W5500. HTTP Server

 

Сегодня мы продолжим тему работы с LAN. Только для этих целей мы возьмём уже другую микросхему — W5500. Эта микросхема разработана компанией Wiznet и интересна она тем, что кроме физического и канального уровня она включает в себя целый аппаратный стек протоколов TCP/IP. Также по сравнению с микросхемой ENC28J60, с которой мы работали долгое время, последовательно и тщательно изучая работу протоколов различных уровней, W5500 обладает гораздо большими вычислительными способностями. Общая память для буферов чтения и записи — 32 килобайта, причём буферы также делятся на сокеты, позволяющие одновременно работать с несколькими соединениями.

Хочется отметить, что у данного производителя, помимо этой микросхемы, в линейке LAN-микросхем с готовым стеком существует ещё несколько подобных чипов, которые различаются по мощности, по интерфейсу подключения и по многим другим параметрам.

Микросхема W5500 отличается от своих собратьев тем, что она имеет для связи с контроллером и для передачи данных между ней и контроллером только интерфейс SPI. Некоторые другие микросхемы данной линейки также для связи имеют и параллельный интерфейс, который позволяет осуществлять более быстрый обмен между данным чипом и МК. Но зато W5500 в отличие от остальных микросхем этой линейки более удобна для широкого использования ввиду того, что корпус имеет меньшее количество ножек, что позволяет легче осуществлять её монтаж на плате.

Также хочется отметить то, что микросхема работает уже в более продвинутых режимах на физическом уровне, нежели ENC28J60. Она поддерживает следующие режимы:

10BT Half-duplex,

10BT Full-duplex,

100BT Half-duplex,

100BT Full-duplex.

Также поддерживается режим Auto-negotiation, который позволяет автоматически переключать скорость в зависимости от скорости того устройства, с которым происходит обмен по LAN.

Режим auto-MDIX в микросхеме к сожалению не реализован. Это, если вы знаете, режим, позволяющий подключать устройства прямым кабелем без использования маршрутизаторов, коммутаторов и свитчей. Обходится это подключением их кросс-оверным кабелем. А если устройство, к которому мы по LAN подключаем данную микросхему, поддерживает такой режим, то мы спокойно можем использовать и прямой кабель.

Ну и теперь самое главное.

Какие же протоколы из стека TCP/IP микросхема поддерживает аппаратно?

Вот их перечень: TCP, UDP, ICMP, IPv4, ARP, IGMP, PPPoE.

А со всеми остальными прелестями и подводными камнями мы будем знакомиться уже по мере программирования данного чипа в нашем проекте.

Как видоно из темы занятия, проект наш будет нацелен на организацию небольшого сервера HTTP, который будет поддерживать пока только одно соединение. Думаю, для начала неплохо. Так как к подобному серверу на микросхеме, рассмотренной нами ранее мы шли на протяжении доброго десятка занятий.

Данную микросхему для проекта мы будем использовать в виде недорогого (как выражаются, народного) модуля, который можно вполне приобрести по цене окло 5 долларов. Думаю, это смешная цена для такого модуля. Производитель — Waveshare, продукцией которого мы очень нередко пользуемся. Выглядит он вот таким образом

 

image02

 

Контроллер для нашего проекта мы будем использовать STM32F401RET6, который расположен на одноимённой плате NUCLEO-F401RE.

Запустим генератор проектов Cube-MX и создадим там новый проект, выбрав контроллер STM32F401RETx.

Настроим тактирование от ST-Link

 

image00

 

Включим программатор для отладки

 

image01

 

Также включим интерфейс SPI для подключения нашего модуля

 

image03

 

Документы для передачи клиента мы будем использовать в виде файлов, расположенных на карте Micro-SD. Так как наш контроллер поддерживает аппаратно SDIO, то мы его и включим

 

image04

 

Соответственно, включим и FATFS

 

image05

 

Включим USART для удобной отладке проекта в реальном времени

 

image06

 

Также включим ножки PB1 и PB6 на выход соотественно для RESET и CS модуля. Именно данные ножки были выбраны для удобства подключения согласно распиновке платы Nucleo

 

image07  image08

 

Теперь настроим параметры в разделе Clock Configuration (нажмите на картинку для увеличения изображения)

 

image09_500

 

Перейдём в раздел Configuration.

Сначала настроим SPI1. Включим пока делитель 4, что позволит работать с шиной SPI со скоростью 18 мегагерц (мегабит в секунду)

 

image10

 

Микросхема поддерживает частоту работы по шине SPI до 80 мегагерц, но это только теоретически, практически проверено до 33,3. Так написано в технической документации. Я правда ставил делитель 2, что соответствовало 36 МГц, и модуль нормально всё передавал и принимал. Но не будем рисковать. Попробовать всегда можно. А то вдруг будут какие глюки из-за этого, а мы начнём думать плохо о своём коде.

 

 

Теперь USART

 

image11

 

Далее FATFS. Как обычно, включим поддержку длинных имён и длинных секторов

 

image12

 

Также немного добавим скорость на работу служебных ножек GPIO

 

image13

 

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

 

image14

 

Перейдём теперь в окно настроек проекта и выберем в качестве среды программирования Keil, а также дадим проекту имя W5500_HTTPS

 

image29

 

Сохраним настройки, сгенерируем проект, откроем его в Keil, настроим программатор на авторезет, и также настроим оптимизацию кода на уровень 1.

Попробуем собрать проект.

Если всё нормально собралось, то начнём сочинять код.

 

 

Проект наш, конечно, может и не собраться ввиду отсутствия файла ccsbcs.c. Возьмём его и подключим из проекта урока 79 ENC28J60_HTTPS_SD.

Теперь проект, скорей всего соберётся.

Далее создадим два файла net.h и net.c для управления интерфейсом LAN следующего содержания:

 

net.h

#ifndef __NET_H

#define __NET_H

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

#include "stm32f4xx_hal.h"

#include <string.h>

#include <stdlib.h>

#include <stdint.h>

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

#define IP_ADDR {192,168,1,197}

#define IP_GATE {192,168,1,1}

#define IP_MASK {255,255,255,0}

#define LOCAL_PORT 80

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

#define be16toword(a) ((((a)>>8)&0xff)|(((a)<<8)&0xff00))

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

#endif /* __NET_H */

 

net.c

#include "net.h"

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

extern UART_HandleTypeDef huart2;

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

uint8_t ipaddr[4]=IP_ADDR;

uint8_t ipgate[4]=IP_GATE;

uint8_t ipmask[4]=IP_MASK;

uint16_t local_port = LOCAL_PORT;

char str1[60]={0};

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

 

В файле main.c также подключим данную библиотеку

 

/* USER CODE BEGIN Includes */

#include "net.h"

/* USER CODE END Includes */

 

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

 

char str1[60]={0};

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

void packet_receive(void)

{

}

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

 

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

 

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

void net_poll(void)

{

  packet_receive();

}

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

 

Создадим для данной функции прототип и вызовем её в бесконечном цикле главного модуля функции main()

 

/* USER CODE BEGIN 3 */

  net_poll();

}

/* USER CODE END 3 */

 

Создадим также в файле net.c функцию инициализации

 

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

void net_ini(void)

{

}

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

 

Создадим прототип и для неё и вызовем её в главном модуле в функции main()

 

/* USER CODE BEGIN 2 */

net_ini();

/* USER CODE END 2 */

 

Создадим также ещё 2 файла w5500.h и w5500.c для более низкого уровня общения с микросхемой со следующим содержимым

 

w5500.h

 

#ifndef W5500_H_

#define W5500_H_

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

#include "stm32f4xx_hal.h"

#include <string.h>

#include <stdlib.h>

#include <stdint.h>

#include "fatfs.h"

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

#define CS_GPIO_PORT GPIOB

#define CS_PIN GPIO_PIN_6

#define SS_SELECT() HAL_GPIO_WritePin(CS_GPIO_PORT, CS_PIN, GPIO_PIN_RESET)

#define SS_DESELECT() HAL_GPIO_WritePin(CS_GPIO_PORT, CS_PIN, GPIO_PIN_SET)

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

#define MAC_ADDR {0x00,0x15,0x42,0xBF,0xF0,0x51}

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

#define be16toword(a) ((((a)>>8)&0xff)|(((a)<<8)&0xff00))

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

#endif /* W5500_H_ */

 

 

w5500.c

 

#include "w5500.h"

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

extern SPI_HandleTypeDef hspi1;

extern UART_HandleTypeDef huart2;

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

extern char str1[60];

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

uint8_t macaddr[6]=MAC_ADDR;

extern uint8_t ipaddr[4];

extern uint8_t ipgate[4];

extern uint8_t ipmask[4];

extern uint16_t local_port;

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

static void Error (void)

{

HAL_UART_Transmit(&huart2,(uint8_t*)"Error!rn",8,0x1000);

}

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

 

Прежде чем мы начнём писать функцию инициализации, немного познакомимся с организацией памяти. Во-первых существуют два типа регистров. Один тип — это регистры общего назначения. Там хранятся настройки и свойства, которые являются общими для всех сокетов. А второй тип — это регистры для сокетов, в которых хранятся настройки и свойства для определённого сокета.

А память организована следующим образом. Всего для данных памяти 32 килобайта, половина которой отведена для принятых данных, а другая половина — для отправляемых. Каждая половина разбивается по умолчанию на 8 частей — по 2 килобайта. Каждая часть предназначена для определённого сокета. По нашему желанию, границы этих буферов могут передвигаться. Например мы можем на нулевой сокет отвести 9 килобайта, а на остальные 7 — по одному или как нам будет угодно. Пока мы оставимм всё по умолчанию и не будем это трогать. И вообще, как я и писал выше, мы будем работать только с одним сокетом, хотя некоторые переменные, массивы и структуры мы будем организовывать для работы с несколькими сокетами, так сказать на будущее (навырост).

Это про память. Теперь по то, как вообще данные передаются по интерфейсу SPI микросхемы. Существуют несколько способов передачи и приёма данных. Но делятся они на два типа. Это отправка и приём фиксированного количества байтов данных (1, 2 или 4 байта) и отправка и приём данных переменного размера. Нам нужны будут оба способа, поэтому давайте посмотрим, как это вообще делается.

Запись пакета данных переменной длины в шину SPI

 

image16

 

Сначала мы передаём 16-битный адрес памяти (регистра), соответственно первый байт — старший, второй — младший. Затем мы передаём код операции, который мы поподробнее рассмотрим ниже, а уж затем — данные друг за другом. Об окончании передачи данных микросхема узнает по поднятой ножке ChipSelect.

Теперь про байт кода операции, который выглядит вот так

 

image17

 

Биты BSB (Block Select Bits) — это указатель на блок, с которым мы работаем. Первые 3 бита — BSB4-BSB2 — содержат на номер сокета, а последние два — блок памяти.

Соответственно блок регистров общих для всех сокетов имеет номер 00000

Например, вот такие номера имеют блоки сокета 0

 

image18

 

А вот такие, например — сокета 6

 

image19

 

Бит RWB (Read/Write Access Mode Bit) кода операции отвечает за тип операции. Если он 1 — то это запись, а если 0 — чтение.

А битовая пара OM (Operation Mode) как раз и отвечает за тип передачи/приёма, то есть, фиксированной длины данных будет обмен или переменной. Вот такие вот их варианты

 

image20

 

Следующий режим — чтение данных переменной длины из шины SPI

 

image22

 

Здесь мы также сначала передаём 2 байта адреса регистра (памяти), затем передаём опкод со сброшенным битом чтения/записи, по которому микросхема узнает, что мы хотим от неё передачи данных, и начнёт нам с этого адреса и далее передавать побайтно информацию из памяти, пока мы не остановим этот процесс поднятием ножки CS.

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

Ну а с самими регистрами мы будем знакомиться по мере написания кода нашего проекта.

 

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

 

 

Предыдущий урок Программирование МК STM32 Следующая часть

 

 

Отладочную плату можно приобрести здесь Nucleo STM32F401RE

и здесь Nucleo STM32F401RE

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

Переходник USB to TTL можно приобрести здесь USB to TTL ftdi ft232rl

 

 

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

 

STM LAN. W5500. HTTP Server

5 комментариев на “STM Урок 91. LAN. W5500. HTTP Server. Часть 1
  1. Александр:

    Урок хороший. но хотелось бы организовать его на 103, т.е. без аппаратной поддержки SDIO. 103 более бюджетный, и для начинающих радиолюбителей он оптимальнее…
    Спасибо за ваши работу…

  2. Александр:

    Спасибо, нашел….

  3. Aleksandr:

    Теперь в функции инициализации в файле w5500.h занесём данные нашего порта в регистры …
    ???
    точно ли в этот файл? похоже не w5500.h а w5500.c

  4. Aleksandr:

    и простите не понял это куда добавлять?

    А теперь напишем код программной перезагрузки модуля в функцию инициализации

    HAL_Delay(70);
    //Soft Reset
    opcode = (BSB_COMMON<<3)|OM_FDM1;
    w5500_writeReg(opcode, MR, 0x80);
    HAL_Delay(100);

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

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

*