[Из песочницы] Веб-сервер за 5 минут на базе PIC и W5100

Все, что вы хотели узнать о том, как за 5 минут запустить простой веб-сервер на чипе W5100, но стеснялись спросить.image

В статье будет просто, подробно и ясно описано, как запустить, например, веб-сервер, на замечательной и недорогой микросхеме W5100 компании Wiznet.

Чем же она замечательна? Во-вторых — недорогая.И во-первых — всю работу она делает за Вас. Вам же остается лишь лениво слать-принимать ТЕКСТОВЫЕ (точнее — HTML) данные.Дисклаймер1: Инженеграм-электронщикам с… дцатилетним стажем статья может показаться поверхностной и упрощенной, поскольку они и так (возможно) в теме. Данная статья рассчитана на тех, кто имеет некоторый опыт в электронике, или, по крайней мере, в программировании микроконтроллеров, и просто, черт подери, хочет, наконец (слитно), запустить эту… (зачеркнуто) (ладно, пусть будет «замечательную») микросхему W5100.

Дисклаймер 2: Я не ставил целью всеобщее обучение, полный перевод даташит и растолковывание всех его пунктов. Моей задачей является прояснить запутанные моменты и показать как пользоваться чипом W5100 и как запустить на нем аппликацию (например — веб-сервер).

На основе моих разъяснений и примера аппликации, каждый, кто имеет опыт в микроконтроллерах и хотя бы основные понятия в сетях — сможет разобраться во всем остальном самостоятельно.

Дисклаймер3: Пожалуйста, не проводите этот эксперимент дома. И желательно, чтобы рядом находился кто-нибудь из взрослых.

SCOPEСтатья не предполагает дать всеобъемлющие знания по обширной сетевой теме, для этого есть мануалы, но предоставит возможность понимать что, в принципе, происходит и зачем, и какую информацию искать для углубленного понимания сабжекта.Однако, представленный в конце пример позволит Вам разобраться, как использовать интернет (точнее — Ethernet и TCP/IP) в своих аппликациях на микроконтроллерах, и, несмотря на простоту, он может быть вполне применен практически. Вы сможете его усложнять, поскольку поймете, как работать со связкой клиент-сервер, и, наконец, имея под рукой что-то работающее и теперь уже много лучше понимая тему — Вам будет намного легче углублять свои познания, не засыпая над десятком страниц с описанием назначения регистров, принципов настройки NAT, или правильным указанием смещения фрагментов в пакетах.

(»… фрагменты в пакетах…» Что то такое вроде у Вуди Аллена было?)

По своему опыту: обычно, человеку, который пишет статью, сабжект ясен и понятен, поэтому он пишет коротко и «ясно».Однако, это часто не так для читателя, который может быть не в теме, и часто вынужден гадать, что же именно автор имел в виду, и следует ли понимать некое высказывание вот так, или совершенно наоборот? Поэтому, там, где можно, я не стану рассусоливать, а в тех местах, где нужно разжевать до полной ясности и однозначности понимания — не стану экономить слов.

При этом, помните, что никто не идеален (даже я), так что простите, если кто ушел неудовлетворенным.

Для дочитавщих до конца — бонус: в конце статьи будет приведен РАБОТАЮЩИЙ код на простом «Си» с кучей комментов, чтоб даже пятикласснику было понятно, что происходит в программе. Код написан для обычного PICa (не все ж еще вокруг AVR-щики и Arduin-щики?).

Ингредиенты Если у вас пока нет особого желания разводить плату и паять на нее микросхему в корпусе TQFP80, рекомендую взять обычный ардуиновский Ethernet Shield на базе W5100. Также потребуется простой и недорогой PIC, макетка для PICa и немного соединительных проводов, Ethernet патч-корд и, по вкусу, сериальный кабель RS232, на случай, если вам вдруг интересно, что происходит внутри сервера во время работы (в код вставлены строки для вывода попутной информации — на терминал).Лирика (можно пропустить) Всем, имеющим некоторый опыт в разработке, эта ситуация знакома (или нет? Или это только я такой лузер?)Вы берет некий чип, на котором необходимо запустить аппликацию. Для простоты допустим, что вам также знакома тема/область, в которой даннаая аппликация будет использоваться (например Ethernet, TCP/IP, HTTP, CGI etc).

Вначале Вы берете даташит на чип, в 100500 страниц,

не пропускать лирику и внимательно его читаете. Затем вы перечитываете его еще несколько раз и начинаете, наконец, понимать, как с данным чипом работать. Затем вы берете errata и повторяете те же итерации.После прочтения оных вы, наконец, понимаете, как чип работает, как его правильно программировать (100500 регистров -1), но пока, все же, остается не совсем ясным, как ПРАВИЛЬНО его надо запускать. (Например, имеет ли значение порядок обращение к регистрам, и если да — то какой порядок правильный, а если не имеет значения — то точно ли он не имеет значения?).

Разумеется, много полезной информации можно найти в инете и на форумах, но из всех найденных там примеров не совсем ясно, какие НА САМОМ ДЕЛЕ работают, какие работают несмотря на обилие ужасающих ошибок (о которых Вы пока и не подозреваете), а какие «примеры» — просто плод фантазии аффтара, который написал, да не проверил, надеясь, что проканает.

Ок, вы прошли все эти этапы, ТЕПЕРЬ Вам уж точно все понятно.

Вы правильно подключаете все пины чипа к чему следует, правильно подключаете чип к процессору, правильно устанавливаете все регистры и что там у него еще внутри, и — о чудо! — наконец-то…Да-да, все в порядке — ничего не заработало.

Это самый противный этап — когда все сделано правильно и строго по даташит, но все равно не работает. Вы долго и муторно разбираетесь, в чем же проблема, и наконец находите 1…5…17 багов. Ну, конечно! Именно об этом и было сказано в даташит/эррате. Ну, теперь-то понятно ЧТО ИМЕННО они там имели в виду (а о чем вы сами могли бы догадаться).

Все.Аппликация заработала.Теперь Вы — спец по данному чипу.Фанфары. Премия. Пиво. Толпы поклонниц. Слава и следующий дидлайн.

Данная статья призвана помочь читателям пропустить нудные этапы и сразу перейти к стадии «все заработало» и далее — по тексту.

Лирика — 2 (можно пропустить) Много лет назад (даже чуть больше), еще в до- Windows98 времена, мой товарищ поварчивал, что вот мол, развелось этих лже-юзеров, DOSовских комманд ничерта не знают, думают, что раз есть Windows 3.11, то в config.sys лезть не обязательно, а туда же, — хотят компьютер иметь.пропустим по Лирике 2? Я всегда считал, что кто-то любит ковыряться во внутрях, а кто-то просто хочет пользоваться и не париться. И оба вида имеют право на существование. (Гай Кавасаки:»… Как микроволновка может выдавать сообщение об ошибке? Микроволновка должна просто разогревать! Никто не должен проходить специальные курсы программирования микроволновок!»).Сегодня часто слышны сетования, что, вот, мол, эти Ардуины портят народ и плодят неучей — нет чтоб отучиться лет n и набраться опыта еще лет n+1 — все бросились клепать свои проекты, не пытаясь получить глубокие знания в программировании и электронике.Ужас, ужас.И что? Я считаю, что это нормально — человек с творческой жилкой и креативом придумал задумку (или наоборот) и хочет ее реализовать. Не имея глубочайших познаний. И это нормально.

Также, на заре интернета, была эдакая мода гордо указывать внизу странички: «сайт создан полностью вручную в notepad.exe. Никаких HTML-редакторов !». Ничего не напоминает?

При этом я, например, начинал не с Си и даже не с ассемблера — первый свой комп, собранный с нуля и абсолютно пустой, чтобы залить в него для начала хотя бы загрузчик, я программировал в двоичном коде 16-тью тумблерами шины адреса и 8-ю тумблерами шины данных.Мне долго еще во сне приходили длинные ряды большик красных нулей и единичек, а когда мама послала купить хлеба, первой мыслью было:

«пойти за хлебом… это какой КОД?».И даже я, баггер со стажем, поддерживаю прогресс, увеличившиеся возможности для творчества новичков, и разнообразие видов юзеров.

Client — Server. В общих чертах Вероятно, есть смысл начать с конца и двигаться наверх.Вы запускаете программу браузера, вводите в ней название сайта — и получаете на экране содержимое веб-странички.Разумеется браузер получает не красивую страничку с картинками, а кучу текста, HTML-тэгов, линки на картинки/видео и т.п., собирает все это вместе, и выдает вам в виде красивой (иногда) веб-странички.

Все это передается между сервером и браузером по протоколу HTTP, который также управляет и установлением соединения между ними и их поведением.

Поскольку мы имеем дело с сетью/интернетом, то, для того, чтобы быть переданными по правилам, принятым в сети/интернете, все эти данные (HTML, вокруг которого «обернут» HTTP) заворачиваются в новый «слой» — TCP/IP.

TCP (Transmission Control Protocol) используется для того, чтобы обеспечить гарантированное установление соединений и доставку пакетов данных. Веб браузеры (как и почтовые клиенты) используют именно TCP.

Вероятно, я должен упомянуть о том, что помимо ТСР существует и UDP (User Datagram Protocol) который вообще никак не озадачивается такой мелочью, как установление соединения и гарантированная доставка сообщений, а раз время на это не тратит, то и строчит пакетами как из пулемета, но использоваться может только если соединение (канал) качественный и надежный, либо если не предъявляются жесткие требования к потере (точнее НЕ потере) пакетов (господи, да кто их считает!).

В данной статье UDP рассматриваться не будет, но W5100 умеет и его.

Поскольку наш веб-сервер будет соединен с компьютером через Ethernet-соединение, все это «оборачивается» в еще один слой — Ethernet frames.

На самом деле, как вы знаете, мы могли бы передавать TCP/IР даже и по обычному RS-232 каналу, безо всякого Ethernet.

Итак, мы имеем матрешку: Ethernet фрейм, состоящий из служебных байтов («заголовков») и байтов с собственно данными (Payload).

Эти данные содержат в себе TCP/IP пакеты, которые, в свою очередь, также состоят из байтов заголовков и собственно данных.

А эти последние данные, в свою очередь, состоят из HTTP-заголовков и давно уже ожидающих и успевших чуть остыть HTML-форматированного текста и/или картинок.

Сказ про Etherтet, ТСР и IP — мы опустим, потому что все эти сложности W5100 берет на себя, снимая с нас головную боль (вот в чем ее прелесть!) (я имею в виду прелесть W5100, не головной боли).

А вот на HTTP остановимся капельку подробнее, потому что именно нам придется самим забирать его у W5100 и обрабатывать (когда веб-браузер будет делать запрос к нашему серверу) и наоборот — формировать его и отдавать W5100, когда мы (сервер) будем отвечать веб-браузеру.

Полностью раскрыть все нюансы HTTP протокола в данной статье не представляется возможным (отсылаю любопытных к HTTP/1.1 RFC 2616), здесь же мы рассмотрим самое основное и то, что поможет нам поднять простой (но вполне юзабельный !) веб-сервер.

Когда мы открываем браузером веб-сайт (например: opticaldt.us — не реклама!) браузер отсылает на веб-сервер сайта запрос:

GET http://opticaldt.us/ HTTP/1.1 Host: opticaldt.us Connection: keep-alive Cache-Control: max-age=0 Accept: text/html, application/xhtml+xml, application/xml; q=0.9, image/webp,*/*; q=0.8 User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US, en; q=0.8, ru; q=0.6

Обратите внимание на эту строку: GET opticaldt.us

После »… us/» и до «HTTP/1.1» — пусто.Это значит, что, по умолчанию, запрашивается файл index.html (или index.htm).Если в данной субдиректории на сервере действительно имеется файл index.html, то браузер успешно его откроет. Если же этого файла нет (либо вообще, вы сделали запрос н отсутствующий файл с любым именем и расширением), то сервер ответит вам страничкой «error 404» (которая также возникает не извоздуха — отдать ее в ответ на неверный запрос — задача сервера. То есть вот прям лично вы должны создать такую страницу).

Как видно из заголовка, клиент (браузер) сообщает о себе много любопытного и полезного.Поскольку наш сервер будет достаточно простым, и ничего такого особенного мы не станем посылать клиенту (например, не станем вытягивать из заголовка все интимные подробности и огорашивать юзера чем-то вроде: «Здравствуйте, вы пришли с такого-то ip, живете по такому-то адресу на втором этаже, хотя квартира вообще то не ваша, счет провайдеру закончится через 3 дня, не забудьте пополнить, вашего кота Федора пора бы покормить, с такой-то резолюцией экрана и WindowsXP негоже заходить на наш восхитительный сайт, который много лучше смотрелся бы, будь у вас не Mozilla, a IE 11, ах да, у вас ведь украинский ip, поэтому «здоровэньки булы», хотя нет, это же Евпатория, поэтому «и снова здравствуйте! И т.д. и т.п.), нас, при запросе от браузера, будет интересовать только первая строка — нам нужно будет ее, как говорят в народе «пропарсить», чтобы выяснить, какой именно файл клиент (браузер) желает получить от нашего сервера.

В простейшем варианте, просто чтоб протестировать, что сервер работает, можно вообще на любой запрос (то есть запрос любого файла) отсылать одну и ту же HTML страницу.(Забегая вперед, в нашем примере мы все же будем отдавать «файл» index.html если запросят именно его, и страницу с ошибкой, если запрашивать будут что-либо иное).

Итак, мы сделали упомянутый выше запрос, по счастливому стечению обстоятельств на данном сайте в данном месте имеется файл index.html, и сервер отвечает браузеру сообщением, состоящим из заголовка и собственно содержимого файла index.html:

HTTP/1.1 200 OK Date: Sun, 13 Jul 2014 21:32:49 GMT Server: Apache Accept-Ranges: bytes Vary: Accept-Encoding Content-Length: 185 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html

W5100 WebServer

ну шо, хелло World?!
В первой строке (»… 200 ОК…») сервер сообщает, что запрос верный (то есть такой файл имеется).В строке «Content-Length: 185» сервер предупреждает клиента (то есть браузер) о длине передаваемого файла (точнее — именно и конкретно о длине веб страницы, которая идет сразу после HTTP заголовков).

Для того, чтобы обеспечить более менее корректную работу с браузером, нам (серверу) надо будет отдавать в ответ (помимо HTML файла) по крайней мере три параметра в заголовке HTTP:

HTTP/1.1 200 OKContent-Type: text/html (если мы посылаем именно это, а не, например, картинку).Content-Length: (посчитаем потом)

На самом деле, современные браузеры весьма либеральны, поэтому можно отбросить и третью строку заголовка. Тем более, что в нашем примере, после отдачи клиенту «веб-страницы», мы разрываем соединение, поэтому клиент (браузер) и так догадается, что он получил все.

Но… Если Ваша страница будет длинной, или, например, вы собираетесь посылать картинки и т.п. — лучше все-таки указывать длину посылаемого блока данных (Content-Length).

Совсем чуть чуть о TCP/IP Когда я писал выше, что W5100 делает за нас всю работу с Ethernet и TCP/IP, я чуть приукрасил действительность. Кое-что (совсем капельку) нам все-таки придется делать самим. А именно: управлять процессом установления соединения и его завершением.Коротко говоря, сервер и клиент общаются пересылкой друг-другу IP пакетов, которые могут содержать «полезные» данные (например — HTML страничка), а могут быть чисто служебными, без «полезных» данных.

Откроем даташит микросхемы W5100 (да-да, теперь уже пора) и рассмотрим, как общаются клиент (браузер) и W5100 в режиме сервера:

image

Вначале W5100 в режиме OPEN открывает/создает сокет (сетевое соединение) и переходит в режим «слушания» (LISTEN), дожидаясь запроса от клиента (т.е. браузера).

Когда запрос от клиента приходит, и между сервером и клиентом устанавливается соединение, сервер (W5100) переходит в режим ESTABLISHED и между сторонами происходит обмен данными.

На заметку: по уставу, максимальный размер данных в IP пакете — 1500 байт. Если ваши данные не укладываются в один пакет (большой размер HTML кода, или нужно передать картинку итп) — придется разбить данные на порции менее 1500 байт и передавать их одну за другой.

Далее, когда стороны высказали друг-другу все, что в душе нагорело, кто-то из них (как и в жизни) первым «бросает трубку», посылая запрос на разьединение (Disconnect Request).

Примечание: не всегда разьединение наступает именно после (полной) передачи данных. Например, у вас медленный канал, или пошли помехи, и браузер устал ждать завершения и прекратил соединение по своему тайм-ауту. Либо что-то не заладилось у веб-сервера и он решил прервать соединение в самом интересном месте. Ну, в общем, всякое ведь в жизни бывает.

После того, как связь между ними разорвана, сервер переходит в режим закрытия соединения (сокета) — CLOSED (не, ну все, что нужно — было сказано/отослано, че еще ждать то?).

Все.

Если вы сторонник мимолетных связей и/или одного раза Вам хватило с лихвой — на этом можно остановиться.Если же вы оптимист по жизни (или просто настойчивый) и полагаете, что к вашему серверу, вполне возможно, будут еще обращаться, и не раз — вам просто следует вернуться к шагу «OPEN» и далее — по картинке. (Порядочные сервера обычно так и поступают). Хотя… может быть в будущем появятся дешевые пластиковые одноразовые сервера? Типа использовал 1 раз — и выбросил?

Можно было бы обойтись и без этого (благодаря W5100), но я все же упомяну. В реальной жизни при каждом обмене акетами между клиентом и сервером и при каждой смене статуса соединения, в пересылаемых пакетах устанавливаются флаги, говорящие о том, что сейчас происходит и как с этим жить.

Возьмем самый обычный IP стек:

Пусть не испугает никого красивое слово «стек», колдуны и маги от IBM-360 и AS-400 специально выдумывают заумные термины, чтобы подавить у простых смертных всякое желание потрогать немытыми руками их Храм. На самом деле это просто протокол, формат передачи данных, в котором расписано какой длины и в каком следуют порядке служебные поля и собственно данные. (Жванецкий:»… и даже болгары не могут понять нашего выражения, что «несмотря на погодные условия» — означает просто дождь!»)

image

Нас интересуют три старших бита в седьмом байте: ФЛАГИ.

Комбинациями этих трех битов формируют следующие флаги: FIN, SYN, RST, PSH, ACK, URG.

FIN — указывает на окончание сессии; SYN — инициализация соединения; RST — прерывание соединения (обычно — в ответ на ошибку); ACK — подтверждение получения (пакета); PSH — упрощенно — требование немедленной высылки подтверждения; URG — указание на архисрочноть данного пакета.

Если вам любопытно (что похвально и полезно), как же в реальности происходит обмен пакетами и какие флаги при этом выставляются и в каких случаях, а ко всему еще — для чего предназначены остальные поля заголовка (хорошо, хорошо, — «стека») — вам придется найти это самостоятельно. В данной статье мы рассматривать это не будем, поскольку W5100 берет всю заботу о пакетах и флагах на себя, мы же имеем дело с уже свершившимся фактом — статусом соединения на данный момент (OPEN, LISTEN, ESTABLISHED, CLOSED).

Краткое, насколько это возможно, описание W5100 Что ж, далее оттягивать этот момент не представляется возможным, пора рассмотреть W5100 с железной и программной сторон.image

W5100 с одной стороны подключается к Ethernet коннектору (через трансформатор и согласующие цепи), с другой — с микроконтроллеру.К микроконтроллеру ее можно подключать (по вкусу и ситуации) либо с использованием шин адреса и данных, либо через SPI интерфейс.Как именно подключать чип — я расписывать не стану, готовых схем с описанием полно в интернете. Тем более (ладно, раскрою секрет), что мы будем в нашем примере использовать готовую плату с этим чипом.

Чип содержит в себе Ethernet PHY и МАС, реализованные в железе обработчики Ethernet фреймов и нескольких протоколов группы IP, позволяет создать до 4 сокетов (сетевых соединений) и содержит RX и ТХ буферы по 8Кбайт памяти каждый, которые можно распределять между каждым сокетом по-братски или по справеделивости (или поровну), выделяя 1,2,4 или 8 КБ на сокет (но не забывая, что размер всего буфера — 8КБ на прием и 8КБ на передачу).

Распределение памяти:

image

Как видно из рисунка:

с адреса 0×0000 по адрес 0×002F расположены общие для всех регистры, с адреса 0×0400 по адрес 0×07FF — 4 одинаковые группы регистров — по одной на каждый сокет, с адреса 0×4000 по адрес 0×5FFF — буфер передачи (8КБ), который вы можете распределить между сокетами по вашему усмотрению, с адреса 0×6000 по адрес 0×7FFF — буфер приема (8КБ), который вы также можете распределить между сокетами по вашему усмотрению.

Все регистры — 8 битные. Большинство можно и читать и писать.

Мы не станем рассматривать все регистры, а только те, которые потребуются нам для создания веб-сервера, однако, Вы легко сможете понять общую идею и далее развить ее под свои конкретные задачи.

Важный момент: в W5100 используется порядок байт, который в народе называется «биг эндиэн» или «сетевой порядок». Глубокий же научный смысл сводится к тому, что сначала идет (передается) старший бит старшего байта и последним — младший бит младшего байта. Это значит, что, например, для записи 4 байт ай-пи адреса Вашего сервера (скажем 192.168.0.155) в предназначенные для этого регистры Source IP Address 0×000F… 0×0012 (SIPR0… SIPR3), Вам следует записать »192» в регистр SIPR0,»168» — в SIPR1,»0» — в SIPR2 и »155» — в SIPR3.

Общие регистры (Общие — значит влияют на работу всего чипа и всех сокетов, тоесть не относятся к конкретному сокету).0×0000 MR — Mode — тут все понятно — через них мы задаем режим работы;0×0001 …0×0004 GAR0…3 — Gateway Address (напр. 192.168.0.254);0×0005… 0×0008 SUBR0…3 — Subnet mask Address (напр. 255.255.255.0);0×0009…0×000E SHAR0…5 — Source Hardware Address (проще говоря — МАС адрес, например 0A 1B 2C 3D 4E 5F).(если Вы не собираетесь наводнить рынок Вашими серверами, то можете указать любой МАС, главное чтобы в вашей сети не было устройств с таким же МАС адресом — нам ведь не нужны конфликты, верно?)

0×000F… 0×0012 SIPR0…3 — Source IP Address — адрес нашего веб-сервера, например: 192.168.0.155;0×001А — RMSR — RX Memory Size — здесь Вы указываете сколько памяти буфера приема придется на каждый сокет;0×001В — ТMSR — ТX Memory Size — здесь Вы указываете сколько памяти буфера передачи придется на каждый сокет;

Регистры сокетов В нашем примере мы будем работать только с одним сокетом и только с теми его регистрами, которые нужны для TCP/IP сервера, поэтому рассмотрим лишь эти регистры:0×0400 S0_MR — Socket0 Mode Register — устанавливаем режим работы данного конкретного сокета;0×0401 S0_CR — Socket0 Command Register– здесь мы будет выставлять команды для управления состоянием соединения;0×0403 S0_SR — Socket0 Status Register — отсюда мы будем читать данные о текущем статусе происходящего;0×0404, 0×0405 S0_PORT0, S0_PORT1 — Socket0 Source Port — порт, по которому доступен наш сервер (если корректнее — порт по которому доступен открытый нами сокет).

Если по-честному, то обращаясь к сервису, мы должны указывать полностью и протокол, и адрес сервера данного сервиса, и номер порта (а также и название файла и полный путь к нему).

Например, если нам нужен файл index.html, находящийся на сервере с адресом 192.168.0.155 с доступом к сокету через порт 123, то в строке URL браузера (то есть в адресной строке) нам надо будет написать:

192.168.0.155:123/index.html (подождите, пока не нажимайте Enter).

Но… Для соединений вида «http://… » (то есть для протокола HTTP) принято выделять порт 80.Поэтому, если мы назначим для Socket0 Source Port порт номер 80, то можем не вводить его в адресной строке браузера, поскольку, если порт не указан, браузер по определению будет обращаться именно к этому порту, в результате URL вида 192.168.0.155/index.html вполне будет работать без ошибок. (Говоря более обобщенно — это справедливо для всех случаев, для которых имеются дефолтные порта– если вы их не указываете при доступе к сервису, клиент идет по дефолтному порту: 80 для HTTP, 23 для Telnet и так далее).

Также, как мы разбирали ранее, если не указывать название запрашиваемого файла, то обеими сторонами принято подразумевать, что речь идет о файле index.html (или index.htm) (или index.php). Поэтому, указав в адресной строке браузера: 192.168.0.155, мы получим верный отклик сервера (то есть, если клиент на запрашивает у нас конкретное имя файла, то мы понимаем, что он ожидает именно файл index) (при этом помним, что если, например, клиент запросил директорию, содержимое которой мы стесняемся показывать всем и каждому (например — картинки с котиками, или скрипты и т.п.) то вполне комильфо будет выдать ему страничку «ошибка 404»).

Ну и чтоб совсем уж сэкономить время на ввод адреса — если не указывать тип протокола, то браузер автоматически будет считать, что речь идет о HTTP (если, конечно, браузер — не IE который любит повы… Ок, не важно, забыли).

Таким образом, просто написав в строке адреса 192.168.0.155 — все стороны поймут, что речь идет о 192.168.0.155:80/index.html

Значит, решено, да? Зададим в нашем сервере адрес порта сокета — 80.При этом не забываем про «биг — эндиэн», поэтому 80 (точнее — 0080) запишется так: S0_PORT0 = 00, S0_PORT1 = 80 (да, и не забудьте — это десятичные »80», а не хекса).

0×0420, 0×0421 S0_TX_FSR0, S0_TX_FSR1 — Socket0 TX Free Size — размер свободного места в буфере передачи Сокета 0;0×0422, 0×0423 S0_TX_RD0, S0_TX_RD1 — Socket0 TX Read Pointer (* только чтение) — указывает в буфере передачи на начало блока данных, которые (данные) мы собираемся послать клиенту.

Тут есть хитрая фишка (Wiznet решила подгорчить канфэтку): если, например, вы прочли Socket0 TX Read Pointer, и он указывает, например, на адрес 0×4200, то… правильно — это совершенно не означает, что вы должны свои данные на передачу писать в буфер начиная именно с этого места. Ага, ага… («если женщина за рулем показывает левый поворот — это вовсе не означает, что она собирается ехать прямо!» ©). Подробнее об этом мы поговорим чуть позже.

0×0424, 0×0425 S0_TX_WR0, S0_TX_WR1 — Socket0 TX Write Pointer — указывает в буфере передачи на конец блока данных, которые (данные) мы собираемся послать клиенту.

Тут необходимо (снова) сделать важное пояснение.Два последних регистра не определяют физические адреса, по которым мы должны записать в буфере блок данных на передачу. Эти адреса мы должны вычислить сами.

Еще одно примечание: допустим, Вы выделили на буфер передачи 2КБ. Вы прочли Socket0 TX Read Pointer и оказалось, что он указывает на последние 10 байт буфера. А Вы вообще то намеревались передать (т.е. записать в буфер) 1000 байт. Что делать? Все просто: вы пишете свои данные на передачу в эти 10 байт, затем переходите в самое начало буфера и продолжаете писать оставшиеся данные дальше (тоесть из 1000 байт Ваших данных, 10 байт разместятся в конце буфера, а остальная порция в 990 байт — в начале буфера).W5100 знает об этом безобразии (собственно говоря, он их и намутил) и потому, дойдя до конца — сам перескочит в начало буфера и терпеливо дочитает оставшуюся порцию.

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

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

На случай, если кто-то вздумает читать даташит, я сохраняю мнемоники констант и переменных максимально близко к даташиту, чтобы вы не путались с моим примером и с описанием в даташит. (Названия регистров я указал в точности, как в даташит; адреса, разумеется, тоже).

Итак, введем для удобства переменные:

S0_TX_BASE — базовый адрес буфера передачи Сокета0.

Если вы посмотрите на картинку распределения памяти, то он вроде бы равен 0×4000.Ну, а вдруг у вас чип W5100 сидит не на нулевом адресе? То есть, например, чиповский «нулевой» адрес смещен в адресном пространстве вашей системы, и в вашей системе (например) нулевой адрес чипа равен 0×1984.Далее: если с Сокетом0 все ясно, то базовый адрес Сокета1 (помимо потенциального смещения адреса чипа) зависит еще и от того, сколько Вы выделили памяти в буфере на каждый сокет.Тоесть можно опять же жестко забить, а можно «по-правильному» прочесть TMSR регистр (в случае буфера передачи), узнать оттуда сколько буферной памяти распределено по каждому сокету, и таким образом «вычислить» где начинается область буферной памяти для каждого сокета.

В общем, я Вас предупредил, а в своем примере я его, для простоты, жестко забил: S0_TX_BASE = 0×4000.

S0_TX_MASK — маска, понадобится нам далее в «расчетах».

Точно также, маску, по-хорошему, надо вычислять для каждого сокета исходя из того, что записано (вами же, кстати) в регистре распределения памяти буфера между сокетами (TMSR).

Я ничего не вычисляю (раньше я думал, что я — лентяй, но, став постоянным читателем Хабра узнал, что все в порядке — у меня просто прокрастинация), на каждый сокет я выделил по 2КБ (которые, как вы помните, равны не 2000 грам, но 2048 байт), соответственно маска — это 2048 -1 = 2047 (0×07FF).

Итого, у нас в примере будет: S0_TX_MASK = 0×07FF.

Теперь добавим путаницы, а затем с честью ее распутаем:

Как я писал выше, регистры (read only) S0_TX_RD0, S0_TX_RD1– это указатель на начало блока Ваших данных на передачу, расположенных в буфере передачи сокета.Если говорить более корректно — это указатель на тот конкретный байт данных, который будет передаваться наружу, в сеть, в данный момент (ну, тоесть не прям щаз пока вы читаете, а когда дело дойдет до передач).И данные будут передаваться до тех пор, пока текущий адрес (в буфере) передаваемого байта не дойдет до конца блока ваших данных, который (указатель на конец блока), записан в паре регистров S0_TX_WR0, S0_TX_WR1.

Тоесть, по факту, W5100 вытаскивает из буфера передачи байт ваших данных, на который указывает пара регистров S0_TX_RD0, S0_TX_RD1, выплевывает его наружу в сеть, затем инкрементирует регистр и переходит к следующему байту, на который этот регистр указывает, и так этот регистр (пара) и инкрементируется, пока не сравняются по значению с парой регистров S0_TX_WR0, S0_TX_WR1, и в этот момент передача данных закончится. (Соответственно, передав все ваши данные, содержимое S0_TX_RD0, S0_TX_RD1 станет равно содержимому S0_TX_WR0, S0_TX_WR1).

Джентльмены, я прощу прощения за всю эту нудятину, очень не хотелось никого грузить, но без этого — никак (умом Визнету не объять). Потерпите еще чуть чуть, это скоро закончится, coffee break совсем близко.

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

Наплодим несколько переменных:

Смещение: S0_TX_OFFSET = make16 (S0_TX_RD0, S0_TX_RD1)

// создаем 16-разрядную переменную «смещение» из двух 8-разрядных регистров-пойнтеров// не забываем, что поскольку «биг-эндиэн», старший байт находится в регистре S0_TX_RD0,//, а младший — в S0_TX_RD1

По жизни, этот пойнтер может указывать вообще за пределы адресного пространства чипа (когда почитаете эти регистры в живой аппликации — убедитесь сами), поэтому надо отрезать лишнее, чтобы ячейка памяти, на которую указывает пойнтер, лежала внутри отведенной для данного Сокета зоны (то есть в физическом диапазоне адресов).Придадим значению регистров более близкую к реальности величину (значение):

S0_TX_OFFSET = (S0_TX_OFFSET & S0_TX_MASK)

Теперь вычислим настоящий, физический адрес, по которому нам надо начинать записывать наши данные на передачу:

S0_TX_Start_Addr = S0_TX_OFFSET + S0_TX_BASE

Ну что ж, теперь у нас есть пойнтер, который указывает чипу с какого места начинать считывать байты на передачу, мы рассчитали физический адрес в буфере передачи нашего сокета (Сокет0), и (допустим) мы уже залили наши данные на передачу — в буфер передачи чипа, начиная с рассчитанного нами физического адреса.

Как чип узнает, до каких пор ему считывать данные из буфера передачи? На это ему укажет упомянутый ранее регистр (пара) — пойнтер Socket0 TX Write Pointer.

Пока что пойнтер чтения Socket0 TX Read Pointer и пойнтер записи Socket0 TX Write Pointer содержат одинаковые значения.Потому что (см. выше) после передачи блока данных их содержимое становится одинаковым, и подозреваю (в даташит об этом ни слова), после ресет чипа — тоже.

Итак, последний штрих — нам необходимо указать на конец блока данных в буфере передачи.Внимание! — в отличие от физического адреса, который нам был нужен чтобы загрузить данные в буфер передачи, здесь мы будем оперировать именно с внутренним чиповским представлением самого себя — в памяти.Тоесть мы просто увеличим содержимое пойнтера записи на длину передаваемого блока данных:

Socket0 TX Write Pointer = Socket0 TX Write Pointer + Длина_Блока_Данных.

Предполагая, что у Вас уже голова кругом идет, напомню, что под «Socket0 TX Write Pointer» (16 бит) мы подразумеваем содержимое пары 8-битных регистров S0_TX_WR0 (старший байт) и S0_TX_WR1 (младший байт).

(И — да, — если устали — можете попить кофе и перекурить, я подожду…)

Стойте! Перед перекуром… Даже не знаю, как вам сказать… Мы сейчас рассмотрели работу с регистрами при передаче данных. Понимаете, дело в том, что с регистрами приема данных — примерно такая же байда — преобразование пойнтеров — в физические адреса, длина блока.Давайте вот как сделаем: принцип то Вы наверное поняли? В примере программы на Си я все этапы буду подробно комментировать, так что не запутаетесь. А если все же что-то останется непонятным — я в комментах отвечу. Deal?

Алгоритм Если у меня и была надежда полу

© Habrahabr.ru