Урок 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, продукцией которого мы очень нередко пользуемся. Выглядит он вот таким образом
Контроллер для нашего проекта мы будем использовать STM32F401RET6, который расположен на одноимённой плате NUCLEO-F401RE.
Запустим генератор проектов Cube-MX и создадим там новый проект, выбрав контроллер STM32F401RETx.
Настроим тактирование от ST-Link
Включим программатор для отладки
Также включим интерфейс SPI для подключения нашего модуля
Документы для передачи клиента мы будем использовать в виде файлов, расположенных на карте Micro-SD. Так как наш контроллер поддерживает аппаратно SDIO, то мы его и включим
Соответственно, включим и FATFS
Включим USART для удобной отладке проекта в реальном времени
Также включим ножки PB1 и PB6 на выход соотественно для RESET и CS модуля. Именно данные ножки были выбраны для удобства подключения согласно распиновке платы Nucleo
Теперь настроим параметры в разделе Clock Configuration (нажмите на картинку для увеличения изображения)
Перейдём в раздел Configuration.
Сначала настроим SPI1. Включим пока делитель 4, что позволит работать с шиной SPI со скоростью 18 мегагерц (мегабит в секунду)
Микросхема поддерживает частоту работы по шине SPI до 80 мегагерц, но это только теоретически, практически проверено до 33,3. Так написано в технической документации. Я правда ставил делитель 2, что соответствовало 36 МГц, и модуль нормально всё передавал и принимал. Но не будем рисковать. Попробовать всегда можно. А то вдруг будут какие глюки из-за этого, а мы начнём думать плохо о своём коде.
Теперь USART
Далее FATFS. Как обычно, включим поддержку длинных имён и длинных секторов
Также немного добавим скорость на работу служебных ножек GPIO
Ну и, что едва ли не самое главное для корректной работы, так как у нас нет в модуле SD подтягивающих резисторов, подтянем их к определённым ножкам шины SDIO
Перейдём теперь в окно настроек проекта и выберем в качестве среды программирования Keil, а также дадим проекту имя W5500_HTTPS
Сохраним настройки, сгенерируем проект, откроем его в 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
Сначала мы передаём 16-битный адрес памяти (регистра), соответственно первый байт — старший, второй — младший. Затем мы передаём код операции, который мы поподробнее рассмотрим ниже, а уж затем — данные друг за другом. Об окончании передачи данных микросхема узнает по поднятой ножке ChipSelect.
Теперь про байт кода операции, который выглядит вот так
Биты BSB (Block Select Bits) — это указатель на блок, с которым мы работаем. Первые 3 бита — BSB4-BSB2 — содержат на номер сокета, а последние два — блок памяти.
Соответственно блок регистров общих для всех сокетов имеет номер 00000
Например, вот такие номера имеют блоки сокета 0
А вот такие, например — сокета 6
Бит RWB (Read/Write Access Mode Bit) кода операции отвечает за тип операции. Если он 1 — то это запись, а если 0 — чтение.
А битовая пара OM (Operation Mode) как раз и отвечает за тип передачи/приёма, то есть, фиксированной длины данных будет обмен или переменной. Вот такие вот их варианты
Следующий режим — чтение данных переменной длины из шины SPI
Здесь мы также сначала передаём 2 байта адреса регистра (памяти), затем передаём опкод со сброшенным битом чтения/записи, по которому микросхема узнает, что мы хотим от неё передачи данных, и начнёт нам с этого адреса и далее передавать побайтно информацию из памяти, пока мы не остановим этот процесс поднятием ножки CS.
Ну а режимы чтения и записи данных фиксированной длинны работают аналогичным образом, только передача/приём осуществляется строгого количества байтов.
Ну а с самими регистрами мы будем знакомиться по мере написания кода нашего проекта.
В следующей части урока мы напишем функцию инициализации микросхемы, а также начнём писать функцию приёма и обработки сетевых пакетов.
Предыдущий урок Программирование МК STM32 Следующая часть
Отладочную плату можно приобрести здесь Nucleo STM32F401RE
Ethernet LAN Сетевой Модуль можно купить здесь W5500 Ethernet LAN
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Урок хороший. но хотелось бы организовать его на 103, т.е. без аппаратной поддержки SDIO. 103 более бюджетный, и для начинающих радиолюбителей он оптимальнее…
Спасибо за ваши работу…
Спасибо, нашел….
поделитесь
Теперь в функции инициализации в файле w5500.h занесём данные нашего порта в регистры …
???
точно ли в этот файл? похоже не w5500.h а w5500.c
и простите не понял это куда добавлять?
А теперь напишем код программной перезагрузки модуля в функцию инициализации
HAL_Delay(70);
//Soft Reset
opcode = (BSB_COMMON<<3)|OM_FDM1;
w5500_writeReg(opcode, MR, 0x80);
HAL_Delay(100);