Введение
Тенденция повсеместного внедрения «Интернета вещей» (IoT, или Internet of Things) постепенно набирает обороты. Интенсивно развивающийся рынок требует технологий связи, которые могут быть легко интегрированы в электронные устройства, не обладающие большими вычислительными ресурсами. Сам термин «Интернет вещей» подразумевает добавление функции связи и обработки данных даже в самые простые бытовые приборы. Спектр применений IoT предполагает такие типовые задачи, как дистанционное управление шлагбаумами и автоматическим воротами, бытовой техникой, системами вентиляции и кондиционирования, системами освещения и т. д. Технология связи, используемая для «Интернета вещей», должна быть гибкой, чтобы легко адаптироваться под любую задачу, и нетребовательной к ресурсам микроконтроллера, чтобы иметь возможность эффективно работать даже на самых низкопроизводительных вычислительных системах и оставаться максимально доступной для массового потребителя.
Компания Microchip давно известна на рынке полупроводниковой техники своими 8‑разрядными микроконтроллерами, которые отличаются простотой освоения и достаточно большим набором периферийных расширений, способных работать без участия вычислительного ядра. В сочетании с оригинальными технологиями eXtreme Low Power (XLP) и picoPower для обеспечения сверхнизкого энергопотребления микроконтроллеры семейств PIC и AVR компании Microchip представляют собой хорошую базу для построения систем «Интернета вещей». В широком спектре микроконтроллеров особенно хотелось бы отметить единственный в своем роде 8‑разрядный микроконтроллер со встроенным Ethernet MAC + PHY — PIC18F97J60, на базе которого можно создать недорогое устройство мониторинга и/или управления с подключением в сеть Ethernet.
Особенностью компании Microchip является не только производство полупроводниковых компонентов, но и разработка программно-аппаратных решений для сокращения времени внедрения и выпуска изделий у производителей готовых устройств. Следует отметить программно-аппаратное решение, на базе которого специалисты могут создать простейшие устройства с применением 8‑битного микроконтроллера, соединенные в одну сеть по проводному интерфейсу Ethernet. Данные устройства, используя технологию PoE (Power over Ethernet), легко интегрируются в существующую инфраструктуру сетей Ethernet внутри здания, не требуя дополнительных инвестиций на прокладку новых кабелей. Такой подход значительно упрощает развертывание сетей связи в самом широком диапазоне применений и делает систему передачи данных менее громоздкой.
Наряду с интересными 8‑битными микроконтроллерами большую ценность данного программно-аппаратного решения представляет легковесный, тонко конфигурируемый программный стек протоколов TCP/IP, способный действовать в составе простейшего микроконтроллерного устройства на минимальном объеме памяти, сохраняя при этом вычислительные ресурсы для исполнения пользовательских приложений. О нем и пойдет речь в статье.
Архитектура стека TCP/IP
Применение библиотеки Microchip TCP/IP Lite Stack library основано на модели взаимодействия протоколов TCP/IP, как показано на рис. 1.
Рис. 1. Многоуровневая модель стека TCP/IP
Стек TCP/IP разделен на несколько уровней, как можно видеть на рис. 1. Каждый уровень в программной библиотеке Microchip TCP/IP Lite Stack library открыт для прямого доступа с одного или нескольких уровней выше или ниже него.
Работа стека TCP/IP основана на взаимодействии с фоновыми задачами, периодически запускаемыми пользователем и генерирующими различные события — управление тайм-аутами, проверка статуса контроллера Ethernet, разбор принятых данных.
Библиотека включает несколько протоколов, программный код каждого из них расположен в отдельном исходном файле, в то время как API-интерфейс описан в заголовочных файлах и подключаемых модулях.
Конфигурирование стека
Microchip TCP/IP Lite Stack является масштабируемым. То есть пользователь может самостоятельно изменять состав стека в зависимости от требований приложения. Среда разработки MCC предоставляет возможность собирать код из нескольких фрагментов, чтобы добиться необходимой комбинации протоколов для работы в составе приложения пользователя. В среде MCC также можно настраивать параметры выбранных протоколов. Параметры содержатся в конфигурационном файле tcpip_config.h, который является частью среды MCC. Среди наиболее значимых параметров, доступных для конфигурирования, находятся:
- Используется протоколом DHCP и содержит вербальную форму имени, ассоциированную с MAC-адресом. Значение по умолчанию «Имя устройства ETHERNET», например — PIC16F18346 ETHERNET.
- Параметр содержит в себе максимальный размер таблицы APR (Address Resolution Protocol). Значение по умолчанию 8.
- Для случая статического IP-адреса — собственно IP-адрес, маска подсети, адрес шлюза по умолчанию, адреса предпочитаемого и альтернативного серверов DNS.
- Для протокола ICMP — специальная опция для включения автоматического ответа в виде эха и сообщения в случае недоступности порта для связи.
Управление буфером стека TCP/IP
Обзор
По умолчанию стек TCP/IP занимает минимальный возможный объем памяти, чтобы оставить как можно больше памяти для пользовательского приложения.
Для того чтобы максимально использовать возможность оптимального распределения памяти, нужно задействовать все необходимые буферы для обмена данными между TCP/IP-соединениями, как будет описано далее.
Контроллер Ethernet принимает и хранит данные до тех пор, пока стек TCP/IP в состоянии их обработать. Буфер для каждого принятого пакета данных управляется автоматически Ethernet-контроллером, а дескриптор буфера доступен для редактирования пользователем.
Ethernet-контроллер начинает автоматически удалять ранее принятые, не обработанные стеком данные из буфера, если памяти буфера становится недостаточно для хранения вновь поступающих пакетов.
Пакеты данных Ethernet, подготовленные для передачи, также помещаются в специально отведенную память контроллера.
Буферы, используемые протоколом UDP
Стек позволяет напрямую передавать пакеты payload в RAM-память Ethernet-контроллера. Для пользователя доступны API-функции по управлению формированием payload-пакетов UDP, их передачей между областями памяти и их отправкой в среду передачи.
Когда данные поступают по протоколу UDP, Ethernet-контроллер автоматически помещает их в буфер. Если пакеты успешно приняты и зарегистрирован вызов от пользователя для входящего порта, стек запускает назначенную пользователем функцию и предоставляет возможность прямого доступа к payload-данным прямо в памяти Ethernet-контроллера. Такой подход исключает необходимость многократного копирования и сохранения payload-данных, позволяет экономить время и память контроллера.
Буферы, используемые протоколами TCP/IP
В случае применения протокола TCP пользователь должен специально отвести определенное количество памяти для каждого TCP-соединения. Есть несколько типов буферов, используемых TCP-протоколом:
Память сокета (socket memory). Память, отведенная для каждого сокета, распределяется пользователем. Задача управления памятью решается с помощью специального набора API-функций. Такой подход актуален для тех случаев, когда информация о всех TCP-соединениях полностью известна.
Буферы приема (Rx) и передачи (Tx). Оба вида буферов для каждого TCP-соединения должны быть созданы пользователем и переданы стеку с помощью специальных API-функций. Каждый сокет может иметь единовременно только один буфер приема (Rx) и один — передачи (Tx). Стек всегда должен иметь свободный буфер Rx для принятия данных от удаленного хоста. Стек способен функционировать без Rx-буфера в течение очень ограниченного интервала времени перед тем, как поступит запрос на повторную передачу пакета.
Особенности и ограничения стека TCP/IP
Стек TCP/IP имеет некоторые ограничения, связанные с небольшим объемом памяти RAM и Flash, доступной для функционирования малопроизводительных устройств, таких как 8‑битные микроконтроллеры. Чтобы получить исчерпывающую информацию обо всех актуальных особенностях и ограничениях, следует обратиться к соответствующим документам release notes, относящимся к среде MCC и библиотеке TCP/IP Lite library.
Запуск демоприложений со стеком TCP/IP
Перечень необходимого программного и аппаратного обеспечения для запуска демоприложений:
- Отладочная плата Curiosity Development Board (DM164137).
- Микроконтроллер PIC16F18346.
- Доступное питание от USB-порта.
- Среда разработки программного обеспечения MPLAB X v3.45 или более новая.
- Компилятор языка Си XC8 v1.41 или более новый.
- Персональный компьютер с операционной системой Windows, Linux или MAC.
- Набор демоприложений TCP/IP.
- Интерфейсная плата Ethernet ENC28J60 Click Board (ETH Click).
- Программное приложение Packet Sender для отправки пакетов TCP или UDP. Приложение может быть загружено по интернет-ссылке.
- Сервер DHCP (без него отладочная плата не сможет получить IP-адрес, и демоприложение UDP не сможет работать). Демоприложение TCP работает только со статическим IP-адресом.
- Ethernet-кабели:
- сквозной — для подключения к роутеру или коммутатору;
- кросс-кабель — для прямого подключения к компьютеру.
Настройки аппаратуры
- Подключаем плату ETH Click к плате Curiosity Development board через коннектор J35.
- Подключаем ENC28J60 к сети Ethernet с помощью соответствующего кабеля. Плата должна иметь возможность подсоединяться к работающему DHCP-серверу для запуска UDP-демоприложения.
- Подключаем питание от USB-порта к плате Curiosity Development Board, используя коннектор J2.
- Для успешного запуска каждого приложения пользователь должен выполнять определенную последовательность действий, кратко описанных далее и более подробно рассмотренных в соответствующем руководстве к данному приложению.
Установка демоприложений для TCP-клиента/сервера с использованием среды MCC
Для создания и запуска приложения, использующего TCP, пользователь должен настроить программный модуль TCP/IP Lite, доступный в среде MCC, как показано далее:
- Запускаем приложение MPLAB X и создаем новый проект для устройства PIC16F18346. Данный демопроект использует PIC16F18346, но его можно легко портировать на любой другой 8‑битный микроконтроллер.
- Открываем среду MCC.
- В разделе Device Resources → Libraries двойным щелчком мыши выбираем модуль TCP/IP Lite, как показано на рис. 2.
- Демоприложение TCP требует некоторых изменений в модуле TCP/IP, как показано на рис. 3:
- UDP — не отмечен;
- DHCP — не отмечен. Пользователь должен предоставить собственную конфигурацию со статическим IP-адресом;
- TCP — отмечен.
- Вкладка Notifications показывает различные типы сообщений (рис. 4):
- WARNING — требует исправить некоторую ошибку внутри кода;
- HINT — помогает пользователю отслеживать процесс генерирования кода;
- INFO — дает пользователю информацию о загруженных модулях.
Вкладка Notifications показывает все особенности в процессе формирования конечного кода.
- Стек TCP/IP Lite требует использования модуля Ethernet MAC и модуля Timer1 (рис. 5):
- в разделе Device Resources → Libraries → Ethernet, двойной щелчок по иконке MAC;
- в разделе Device Resources → Peripherals → Timer, двойной щелчок по иконке TMR1.
- Устройство должно быть сконфигурировано на использование системных часов с секундной меткой. Пользователю следует настроить часы Timer1, как показано на рис. 6:
- Timer Period = 250 ms;
- Enable Timer Interrupt = Отмечено;
- Callback Function Rate = 4.
- Далее приведены основные шаги для конфигурирования модуля Ethernet MAC (рис. 7). В окне Easy Setup пользователю следует выбрать тип контроллера ENC28J60 в выпадающем списке. Контроллер ENC28J60 использует интерфейс SPI. Отладочная плата Curiosity Development board (DM164137) поддерживает интерфейс MSSP1 — SPI, выведенный на коннекторе J35.
- Пользователю следует выбрать модуль MSSP1 в списке, как показано на рис. 8:
- в разделе Device Resources → Peripherals → MSSP, двойной щелчок мыши по иконке MSSP1.
- Шаги, необходимые для настройки SPI-модуля MSSP1, показаны на рис. 9:
- Mode = SPI Master;
- Input Data Sampled at = End;
- Clock Edge = Active to Idle.
- Далее следует настроить модуль ADC (рис. 10) для передачи показаний потенциометра через TCP-соединение:
- в разделе Device Resources → Peripherals → ADC, двойной щелчок по иконке ADC.
- Процесс настройки модуля ADC проиллюстрирован на рис. 11:
- Clock Source = FOSC/4;
- Result Alignment = Right.
- Теперь необходимо настроить назначение выводов с помощью раздела меню Pin Module (рис. 12):
- Настройка выводов MSSP1:
- SCK1 — output Port RB6;
- SDI1 — input Port RB4;
- SDO1 — output Port RC7.
- Настройка выводов модуля MAC:
- ETH_CS — для контроллера ENC28J60 — output Port RC6.
- Настройка выводов модуля ADC:
- ANx — input Port RC0;
- в разделе Project Resources → System → Pin Module пользователь может задать любое имя Custom Name для канала AN0, например Pot.
- Настройка интерфейсов LED на плате Curiosity board:
- Pin module — output Port RA5;
- вразделе Project Resources → System → Pin Module пользователь может задать имя Custom Name для вывода RA5, например
- Теперь все пользовательские настройки заданы, и можно нажать кнопку Generate.
Рис. 2. Выбор библиотеки TCP/IP Lite
Рис. 3. Конфигурирование стека
Рис. 4. Вкладка уведомлений (Notifications)
Рис. 5. Выбор модулей MAC и TMR1
Рис. 6. Окно настройки модуля TMR1
Рис. 7. Окно настройки модуля MAC
Рис. 8. Выбор модуля MSSP1
Рис. 9. Настройки модуля MSSP1
Рис. 10. Выбор модуля ADC
Рис. 11. Настройки модуля ADC
Рис. 12. Настройка конфигурации выводов
Простое демоприложение TCP-клиента
Обзор
Простое демоприложение, реализующее функции TCP-клиента, подключающегося к серверу, работающему на порте № 60 персонального компьютера. Пользователю следует задать IP-адрес в прошивке. Как только соединение клиента с сервером установлено, клиент будет автоматически посылать на сервер пакеты своего статуса с периодом в 2 с. Пакеты, посылаемые клиентом, будут содержать значение потенциометра и состояние LED-индикаторов. С помощью сервера пользователь может управлять LED-светодиодами на плате (вкл./выкл.), используя программные GUI-кнопки. В демоприложении каждая GUI-кнопка связана с соответствующим светодиодом.
В демоприложении используется только одно активное TCP-под-ключение. На самом деле стек TCP/IP поддерживает множественные подключения через TCP-протокол на одной плате. Для каждого нового подключения пользователь должен создать отдельный сокет, буфер приема Rx и попытаться подключиться к серверу. Демоприложение настроено для запуска на отладочной плате Curiosity board.
Демо-TCP-клиент будет автоматически пытаться подключиться к серверу каждые 2 с. Это сделано, чтобы пользователю было проще перехватывать и анализировать пакеты с помощью приложения Wireshark protocol analyzer.
Программные настройки для демо-TCP-клиента
- Прежде всего пользователю следует определить IP-адрес компьютера, на котором запущено демоприложение Java.
- Далее используем TCP/IP Lite library из созданного ранее проекта.
- Добавляем в новый проект файлы tcp_client_demo.c и tcp_client_demo.h.
- Внутри функции main() основного программного модуля проекта следует вызвать функцию TCP_Client_Initialize() для добавления IP-адреса и номера порта. Пример конфигурирования IP-адреса и номера порта приведен в листинге 1.
remoteSocket.addr.s_addr = MAKE_IPV4_ADDRESS(192,168,0,3);
remoteSocket.port = 60;
Листинг 1. Пример конфигурирования IP-адреса и номера порта
- В функции main() пользователь должен задать глобальные и периферийные прерывания.
- В цикле while(1) следует вызывать команду Network_Manage(), которая периодически опрашивает Ethernet-контроллер на наличие пакетов данных и обрабатывает их.
- В цикле while(1) следует вызывать команду DEMO_TCP_Client(), которая управляет состоянием сокета, через который клиент подключен к серверу.
- Проект должен быть скомпилирован с использованием компилятора XC8.
- С помощью среды MPLAB X пользователь может загрузить скомпилированную прошивку в плату Curiosity Development board, построенную на базе PIC16F18346.
- Если в среде MCC задан IP-адрес платы, валидный IP-адрес компьютера, на котором работает TCP/IP-сервер, оба IP-адреса уникальны и находятся в одной сети, то становится возможным обмен пакетами данных. Пользователь может проверить наличие связи с использованием сообщений ICMP (с помощью команды пингования).
- Теперь можно запустить демонстрационное Java-приложение на ПК для начала тестирования возможностей стека TCP/IP, как показано на рис. 13.
- Переходим ко вкладке TCP Server Demo.
- В поле Server → Local Port изменяем номер порта на 60.
- Нажимаем кнопку Listen (статус приложения изменяется на Listen, а надпись на кнопке изменяется на Disconnect).
- Когда плата Curiosity board подключится к компьютеру, в поле Sent/Received Data появится сообщение, приведенное в листинге 2.
Рис. 13. Окно демоприложения для персонального компьютера
192.168.0.21: Connected
Листинг 2. Сообщение в поле Sent/Received Data
- В поле Send пользователь может набирать текстовые сообщения для отправки на плату. Сообщение будет отправлено, когда пользователь нажмет клавишу Enter или кнопку Send в окне программы. Отправленные и полученные данные отображаются разными цветами в поле Sent/Received Data.
- Сообщения, приходящие от платы, автоматически выводятся в окно Sent/Received Data и содержат информацию о состоянии светодиодов на плате и строку со значением встроенного потенциометра. Все данные приведены в 16‑ричном формате.
- Нажатие программных кнопок LED 0–LED 7 инициирует отправку на плату команд управления светодиодами. Каждая кнопка включает одноименный светодиод. Единовременно можно включать или отключать только один светодиод.
- Нажатие программной кнопки Disconnect приведет к закрытию соединения с сервером и появлению сообщения Server closed вслед за сообщением Client disconnected. Отдельное сообщение будет выведено для каждого сообщения Client connected.
- Шаги 13–18 могут быть использованы для тестирования возможностей соединения.
Прошивка для демоприложения TCP-клиента: создание буфера
Прежде чем начинать работу с клиентской станцией, пользователь должен создать по меньшей мере сокет и приемный Rx-буфер, как показано в листинге 3.
// create the socket for the TCP Client
tcpTCB_t port60TCB;
// create the TX and RX buffers
uint8_t rxdataPort60[50];
uint8_t txdataPort60[80];
Листинг 3. Сокет и приемный Rx-буфер
Буфер передачи Tx должен создаваться и передаваться TCP-стеку пользователем в момент готовности данных к передаче.
Применение TCP-клиента
Для создания и применения TCP-клиента требуются следующие шаги.
- Инициализация TCP-стека. Функция инициализации TCP_Init() должна быть вызвана перед тем, как начнут использоваться все остальные функции TCP-стека. Обычно инициализация выполняется в автоматическом режиме с помощью функции Network_Init(), расположенной внутри функции SYSTEM_Initialize().
- Установка IP-адреса и номера порта. Пользователь должен указать номер порта, к которому собирается подключаться (это номер порта, который будет прослушивать сервер на наличие передаваемых данных), и IP-адрес компьютера, на котором запущено Java-приложение TCP/IP-сервера. Функция задания номера порта и IP-адреса должна быть вызвана перед началом цикла while(1):
TCP_Client_Initialize();
- Проверка статуса сокета:
socket_state = TCP_SocketPoll(&port7TCB);
Функция проверяет, что указатель, переданный в качестве параметра, зарегистрирован внутри TCP/IP-стека как сокет. Если указатель является валидным сокетом, то функция возвращает его статус. Возможные состояния статуса перечислены в заголовочном файле tcpv4.h.
- Инициализация сокета с целью создания подключения. Вся необходимая информация содержится внутри данной функции:
TCP_SocketInit(&port60TCB);
- Задание локального порта для клиента. Этот шаг необязателен, поскольку TCP-стек автоматически использует ближайший доступный номер порта в качестве локального порта. Пользователь должен использовать функцию TCP_Bind(), чтобы узнать, какой именно номер порта был выбран стеком после инициализации соединения. Этот прием может быть полезен только в случае автоматического выбора номера порта стеком:
TCP_Bind(&port60TCB, 1024);
- Добавление приемного Rx-буфера для активированного сокета. Следующая функция автоматически привяжет буфер к сокету. Буфер будет использоваться для хранения поступающих данных:
TCP_InsertRxBuffer(&port60TCB, rxdat aPort60, sizeof(rxdataPort60));
- Включение TCP/IP-клиента с помощью функции TCP_Connect(), которая запустит процедуру подключения к серверу. Если установка соединения пройдет успешно, пользователь получит возможность обмена данными с сервером через TCP-соединение:
TCP_Connect(&port60TCB, &remoteSocket);
- Закрыть TCP-соединение. Если соединение не удалось или прекратилось в аварийном режиме, то пользователь должен принудительно выполнить процедуру его завершения и закрыть сокет. Только после этого можно пытаться инициировать новое TCP-соединение с интервалами между попытками не менее 2 с.
Следующая функция автоматически закроет неудавшееся или аварийное TCP-подключение. Параллельно пользователь должен периодически проверять состояние TCP-подключения до тех пор, пока он не убедится в его закрытии. После успешного закрытия соединения приемный Rх- и передающий Tx-буферы могут быть безопасно использованы повторно:
TCP_Close(&port60TCB);
- Проверка корректности передачи содержимого буфера Tx. Данная функция должна обязательно вызываться перед тем, как послать в буфер новую порцию данных, потому что сокет может оперировать единовременно только одним буфером:
TCP_SendDone(&port60TCB);
- Чтение принятых данных из Rx-буфера и контроль доступности буфера для использования. Следующая функция возвращает количество байт, имеющихся в Rx-буфере для чтения. Сразу после вызова данной функции буфер перестанет быть доступным для хранения вновь поступающих данных. Поэтому для сохранения новых данных и избегания повторных передач пользователю следует создать новый буфер и как можно быстрее передать его в стек:
rxLen = TCP_GetReceivedData(&port60TCB);
- Отправка содержимого Tx-буфера удаленному серверу. Интерфейс API позволяет пользователю производить отправку данных через активное TCP-соединение. Данные не смогут быть переданы, пока соединение между клиентом и сервером не установлено:
TCP_Send(&port60TCB, txdataPort60, txLen);
- Удаление сокета. Если сокет закрыт и пользователь намерен удалить его из внутреннего списка сокетов, он может использовать следующую API-функцию:
TCP_SocketRemove(&port60TCB);
- Фоновая задача. Следующая функция должна периодически вызываться, чтобы перехватывать тайм-ауты от TCP-стека:
TCP_Update(); // handle timeouts.
Исходный код приложения для TCP-клиента
Демокод приложения TCP-клиента (исходный код и скомпилированный бинарный файл) доступен для загрузки с сайта www.microchip.com. Исходный код приведен в листинге 4. Блок-схема кода показана на рис. 14.
Рис. 14. Блок-схема демопрограммы TCP-клиента
void DEMO_TCP_Client(void)
{
// create the socket for the TCP Client
static tcpTCB_t port60TCB;
// create the TX and RX Client's buffers
static uint8_t rxdataPort60[50];
static uint8_t txdataPort60[80];
static time_t t_client = 0;
static time_t socketTimeout = 0;
uint16_t rx_len;
sockaddr_in_t remoteSocket;
socketState_t socketState;
rx_len = 0;
socketState = TCP_SocketPoll(&port60TCB);
time(&t_client);
switch(socketState)
{
case NOT_A_SOCKET:
// Inserting and initializing the socket
TCP_SocketInit(&port60TCB);
break;
case SOCKET_CLOSED:
// if the socket is closed we will try to connect again
// try to connect once at 2 seconds
socketTimeout = t_client + 2;
TCP_InsertRxBuffer(&port60TCB,rxdataPort60, sizeof(rxdataPort60));
TCP_Connect(&port60TCB, &remoteSocket);
break;
case SOCKET_IN_PROGRESS:
// close the socket
if(t_client >= socketTimeout)
{
TCP_Close(&port60TCB);
}
break;
case SOCKET_CONNECTED:
// implement an echo client over TCP
// check if the previous buffer was sent
if (TCP_SendDone(&port60TCB))
{
rx_len = TCP_GetReceivedData(&port60TCB);
// handle the incoming data
if(rx_len > 0)
{
/* …………………………………………………………………………….
LED Command parsing and LCD updates was removed from this example.
The full code is available in the source code.
…………………………………………………………………………….
*/
// reuse the RX buffer
TCP_InsertRxBuffer(&port60TCB, rxdataPort60, sizeof(rxdataPort60));
}
if(t_client >= socketTimeout)
{
// send board status message only once at 2 seconds
socketTimeout = t_client + 2;
/*
…………………………………………………………………………….
Composing the TX message in the TX buffer was removed from this example.
The full code is available in the source code.
…………………………………………………………………………….
*/
//send data back to the source
TCP_Send(&port60TCB, txdataPort60, strlen(txdataPort60));
}
}
break;
case SOCKET_CLOSING:
//remove the used socket form the list
TCP_SocketRemove(&port60TCB);
break;
default:
break;
}
}
Листинг 4. Исходный код для приложения TCP-клиента
Демоприложение TCP-сервера
Обзор
Простое приложение TCP — эхо сервера, прослушивающего данные на порте № 7. Сервер запускается на отладочной плате Curiosity board и ожидает любых входящих подключений. Сервер возвращает эхо-ответ для всех входящих данных сразу, как только устанавливается соединение с клиентом. Для данного демоприложения возможна единовременная установка только одного активного соединения. Однако стек TCP/IP поддерживает возможность множественных подключений на одной отладочной плате. Пользователь должен создавать новый сервер (создание Rх/Tx-буферов, инициализация и запуск прослушивания данных) для каждого нового соединения.
Настройки программного демоприложения TCP-сервера
- Пользователь должен создать проект, как это было показано в разделе «Установка демоприложений для TCP-клиента/сервера с использованием среды MCC», исключив шаги 11 и 12. Серверу не требуется задействование модулей АЦП и блоков LED-индикаторов.
- Добавляем файлы c и tcp_server_demo.h к созданному проекту.
- Внутри функции mail() пользователю необходимо активировать глобальные и периферийные прерывания.
- В цикле while(1) должна вызываться API-функция Network_Manage(), которая опрашивает Ethernet-контроллер, перехватывает и обрабатывает пакеты данных.
- В цикле while(1) должна вызываться API-функция DEMO_TCP_EchoServer() для открытия сокета и прослушивания входящих клиентских подключений.
- Проект следует компилировать с помощью XC8.
- Используя среду MPLAB X, пользователь может запрограммировать прошивку в микроконтроллер PIC16F18346 на плате Curiosity Development board.
- Запускаем TCP/IP Java-демоприложение на компьютере, как показано на рис. 15.
- Переходим на вкладку TCP Client Demo в окне приложения.
- Редактируем окно Server IP Address и устанавливаем значение IP-адреса для порта № 7. IP-адрес является статическим и может быть изменен также через среду MCC.
- Нажимаем программную кнопку Connect.
- Когда компьютер подключится к плате Curiosity board, в окне Sent/Received Data появится сообщение, как показано в листинге 5.
Рис. 15. Вкладка TCP Client Demo в окне тестового приложения для компьютера
Connected to 192.168.0.21 Port: 7
Листинг 5. Сообщение о подключении к плате Curiosity board
- Далее пользователь может печатать любые данные в окне Send и отправлять их нажатием клавиши Enter или програм-мной кнопки Send. Отправленные и полученные данные будут отображаться разными цветами в окне Sent/Received Data.
- Нажатие программной кнопки Disconnect приведет к закрытию TCP-соединения. Сообщение Connection closed будет выведено по завершении TCP-соединения.
- Можно повторить шаги 9–13 для тестирования TCP-соединения при отправке строк данных разной длины.
- Чтобы сгенерировать трафик TCP-соеди-нения, следует нажать кнопку ECHO back received message. Поле Send должно быть предварительно заполнено данными, которые предполагается отправлять на отладочную плату. Нажатие программной кнопки Send инициирует обмен данными. Остановить трафик обмена данными можно повторным нажатием кнопки Echo Back received message. В этом случае в поле Sent/Received Data будут отображаться только принятые данные.
Демопрошивка TCP-сервера: создание буфера
Перед запуском сервера пользователь должен создать по меньшей мере сокет и буфер приема Rx. Буфер передачи пользователю надо создать и передать TCP-стеку, когда появятся данные, готовые для передачи.
Применение TCP-сервера
Далее приведены шаги, необходимые для запуска и применения TCP-сервера.
- Инициализация TCP-стека. Данная функция должна быть вызвана перед вызовом любых других TCP-функций:
TCP_Init();
Инициализация выполняется автоматически функцией Network_Init(), вызываемой внутри функции SYSTEM_Initialize() среды MCC.
- Проверка статуса сокета. Функция проверяет указатель, передаваемый в качестве параметра, на предмет его регистрации внутри TCP-стека в качестве сокета: socket_state
= TCP_SocketPoll(&port7TCB);
Если сокет является валидным, то функция возвращает его статус. Возможные значения статуса приведены в заголовочном файле tcpv4.h.
- Передача и инициализация сокета с целью создания подключения. Вся необходимая информация для TCP-подключения содержится в данной функции:
TCP_SocketInit(&port7TCB);
- Связывание номера локального порта с сервером. Данная функция связывает локальный порт с прослушиваемым сокетом:
TCP_Bind(&port7TCB, 7);
Сервер будет прослушивать входящие соединения на данном порте. TCP-стек автоматически выделит номер порта, если он не предоставлен пользователем.
- Добавление приемного буфера Rx к сокету. Следующая функция привяжет буфер к сокету для хранения принимаемых данных:
TCP_InsertRxBuffer(&port7TCB, rxdataPort7, sizeof(rxdataPort7));
- Запуск TCP-сервера. Данная функция заставляет TCP-стек прослушивать порт на запросы входящих подключений:
TCP_Listen(&port7TCB);
Если TCP-соединение успешно устанавливается, то пользователь получает возможность обмениваться данными с удаленным узлом. Только один запрос на подключение может быть принят единовременно в рамках действующего демоприложения. Однако TCP-стек способен работать с множественными подключениями для одного порта. Но для каждого подключения пользователь должен создавать отдельный сокет, Rx-буфер и экземпляр TCP-сервера на том же порте.
- Проверка статуса сокета. Данная функция проверяет, является ли указатель, переданный в качестве параметра, сокетом, зарегистрированным TCP/IP-стеком:
socket_state = TCP_SocketPoll(&port7TCB);
Если сокет является валидным, то функция возвращает его статус. Возможные значения статуса приведены в заголовочном файле tcpv4.h.
- Проверка корректности отправки содержимого буфера передачи Tx (это означает, что удаленный узел отправил подтверждение полученных данных):
TCP_SendDone(&port7TCB);
Эта функция должна вызываться каждый раз перед попыткой отправить что-либо, потому что сокет способен оперировать только одним буфером в каждый момент времени.
- Проверка новых данных в буфере приема Rx. Данная функция возвращает количество байт, содержащихся в Rx-буфере:
rxLen = TCP_GetRxLength(&port7TCB);
- Подсчет числа байт данных, доступных для чтения в Rx-буфере, и подготовка буфера для использования. Следующая функция возвращает количество байт данных, доступных в буфере:
rxLen = TCP_GetReceivedData (&port7TCB);
После вызова данной функции пользователь может получить безопасный доступ к буферу. Сразу после вызова данной функции буфер становится недоступен для принятия вновь поступающих данных. Чтобы избежать повторов передачи от удаленного узла, пользователь должен предоставить стеку новый свободный буфер.
- Отправка содержимого буфера передачи удаленному Tx-узлу. API-функции позволяют пользователю осуществлять передачу данных через активное TCP-соеди-нение:
TCP_Send(&port7TCB, txdataPort7, txLen);
Невозможно передать данные, если предварительно не было установлено активное соединение между локальной машиной и удаленным хостом.
- Закрытие TCP-соединения:
TCP_Close(&port7TCB)
Закрытие соединения произойдет только после обмена дружественным рукопожатием между участниками соединения. Пользователю следует периодически проверять состояние сокета до тех пор, пока сокет не окажется закрыт. После закрытия сокета буферы приема и передачи могут быть безопасно использованы снова.
- Удаление сокета. Если после закрытия сокета пользователь желает удалить его из внутреннего списка сокетов, то он должен использовать следующую функцию:
TCP_SocketRemove(&port7TCB)
- Фоновая задача. Данная функция должна периодически запускаться приложением, чтобы перехватывать тайм-ауты от TCP-стека. Запуск функции должен происходить один раз в секунду.
TCP_Update();// handle timeouts
Исходный код приложения TCP-сервера
Демокод TCP-сервера (исходный код и откомпилированный бинарный файл) доступен для загрузки по адресу www.microchip.com. Исходный код приложения приведен в листинге 7. Блок-схема кода показана на рис. 16.
Рис. 16. Блок-схема демопрограммы TCP-сервера
void DEMO_TCP_echo_server(void)
{
// create the socket for the TCP Server static tcpTCB_t port7TCB;
// create the TX and RX buffers static uint8_t rxdataPort7[20];
static uint8_t txdataPort7[20];
uint16_t rxLen, txLen, i;
socket_state_t socket_state;
rxLen = 0;
// checking the status of the socket
socket_state = TCP_SocketPoll(&port7TCB);
switch(socket_state)
{
case NOT_A_SOCKET:
//Inserting and initializing the socket
TCP_SocketInit(&port7TCB);
case SOCKET_CLOSED:
//configure the local port
TCP_Bind(&port7TCB, 7);
// add receive buffer
TCP_InsertRxBuffer(&port7TCB,rxdataPort7, sizeof(rxdataPort7));
// start the server
TCP_Listen(&port7TCB);
break;
case SOCKET_CONNECTED:
// check if the buffer was sent, if yes we can reuse the
//buffer
if(TCP_SendDone(&port7TCB))
{
// check to see if there are any received data
rxLen = TCP_GetRxLength(&port7TCB);
if(rxLen > 0)
{
//make sure it safe to use the receive buffer
rxLen = TCP_GetReceivedData(&port7TCB);
//simulate some buffer processing copy from
//the RX buffer to the TX buffer
for(i = 0; i < rxLen; i++)
{
txdataPort7[i] = rxdataPort7[i];
}
// reuse the rx buffer
TCP_InsertRxBuffer(&port7TCB,rxdataPort7, sizeof(rxdataPort7));
txLen = rxLen;
//send data back to the source
TCP_Send(&port7TCB, txdataPort7, txLen);
}
}
break;
case SOCKET_CLOSING:
TCP_SocketRemove(&port60TCB);
break;
default:
// we should not end up here
break;
}
}
Листинг 7. Исходный код приложения TCP-сервера
Демоприложение протокола UDP
Обзор
В данном примере рассмотрено применение клиента и сервера, работающих по протоколу UDP. Пример состоит из части UDP Send (UDP Client) и UDP Receive (UDP Server). В качестве части UDP Send отладочная плата Curiosity Development отправляет значения встроенного потенциометра в пакетах UDP-протокола. В качестве части UDP Receive отладочная плата Curiosity Development board слушает входящие UDP-подсоединения, включая команды управления светодиодными LED-индикаторами, на порте № 65531. Номер порта может быть любым в диапазоне 0–65535. Но некоторые номера зарезервированы или уже зарегистрированы для использования по умолчанию. Поэтому рекомендуется выбирать номер порта в диапазоне 49152–65535, поскольку все они гарантированно свободны.
Установка программного обеспечения для демоприложений UDP Send/UDP Receive:
- Запускаем MPLAB X и создаем новый проект для устройства PIC16F18346.
- Загружаем модуль MCC.
- Заходим в раздел Device Resources и в подразделе Libraries выбираем пункт TCP/IP Lite module.
- Загружаем TCP/IP Lite module.
- Вкладка Notifications содержит различные виды сообщений:
- WARNING — означает присутствие явных ошибок внутри кода;
- HINT — отражает ход генерации программного кода;
- INFO — содержит информацию о загружаемом модуле.
- Идем в раздел Device Resources и в подразделе Libraries выбираем Ethernet → MAC module.
- Загружаем MAC module.
- В разделе Easy Setup в выпадающем списке выбираем контроллер ENC28J60.
- Контроллер ENC28J60 содержит встроенный интерфейс SPI. Следовательно, согласно схеме платы Curiosity Development board интерфейс MSSP1 — SPI выведен на коннектор J35.
- Заходим в раздел Device Resources → Peripherals → MSSP и двойным щелчком мыши активируем интерфейс MSSP1.
- Загружается программный модуль MSSP1. В модуле следует установить параметр Mode = SPI Master, параметр Clock Edge = Active to Idle, параметр Input Data Sampled = End.
- Заходим в раздел Device Resources → Peri-pherals → Timer и выбираем пункт TMR1.
- В загрузившемся модуле TMR1 устанавливаем Timer Period = 250 ms. Активируем Timer Interrupt и устанавливаем Callback Function Rate = 4, чтобы получить Timer Period = 1 с. Это необходимо, чтобы синхронизировать работу устройства с секундной меткой времени.
- Аналогичным образом необходимо загрузить и сконфигурировать программный модуль ADC, чтобы отправлять данные потенциометра через UDP-соединение. Заходим в раздел Device Resources → Peripherals → Двойным щелчком мыши по значку ADC загружаем одноименный модуль.
- Вмодуле ADC устанавливаем Clock Source = FOSC/4 и Result Alignment = right.
- Конфигурируем менеджер выводов Pin Manager:
- Конфигурация выводов MSSP1:
- SCK1 — output Port RB6;
- SDI1 — input Port RB4;
- SDO1 — output Port RC7.
- Конфигурация выводов MAC:
- ETH_CS (for the ENC28J60 control-ler) — output Port RC6.
- Конфигурация выводов ADC:
- ANx — input PORT RC0;
- в разделе Project Resource → System → Pin Module пользователь может задать произвольное имя для канала AN0, например Pot.
- Настройка выводов LED-индикатора для платы Curiosity board:
- Pin module — output Port RA5;в разделе Project Resource → System → Pin Module пользователь может задать произвольное имя для вывода RA5, например Toggle_Led.
- Нажимаем программную кнопку Generate, чтобы запустить процесс генерирования кода программы.
- Добавляем в проект файлы c и udp_demo.h.
- В функции main() пользователь должен активировать глобальные и периферийные прерывания.
- В функции main() вызываем API-функ-цию POT_UDP_Initialize(), находящуюся в файле c, для инициализации потенциометра и пакетов UDP-сервера: UDP server (destination) Port Number = 65531, UDP Server Address = IP-адрес компьютера (Destination address), UDP Client (Source) Port Number = 65533.
- Внутри цикла while (1) вызываем API-функцию Network_Mange(), чтобы опрашивать Ethernet-контроллер на появление новых пакетов данных и обрабатывать их.
- Проект демоприложения должен быть скомпилирован в среде XC8 для контроллера PIC16F18346, установленного на плате Curiosity Development board.
- Запускаем приложение Packet Sender на компьютере, как показано на рис. 17.
- Заходим в меню File → Settings → Network, активируем параметр UDP Server и выставляем номер порта 65531.
- Поворачиваем ручку на плате Curiosity Development board. Плата автоматически посылает пакеты со значением потенциометра в вольтах.
- В приложении Packet Sender можно отправлять UDP-пакеты для управления светодиодными LED-индикаторами на плате Curiosity Development board (рис. 18):
- Name = UDP Led1 Send;
- ASCII = L1;
- Address = IP-адрес платы. Адрес платы можно посмотреть с помощью утилиты Wireshark analyzer tool в разделе D‑Discover → O‑Offer → RRequest → A‑Acknowledgment;
- Port = 65531;
- выбрать протокол UDP;
- нажать программную кнопку Save для сохранения пакета.
- Нажимаем программную кнопку Send для отправки сохраненного UDP-пакета плате Curiosity Development board и наблюдаем работу светодиодных индикаторов на ней.
- Повторяем шаги 25 и 27, чтобы убедиться, что между компьютером и отладочной платой происходит обмен данными по UDP-протоколу через порт 65531.
Рис. 17. Окно демоприложения Packet Sender
Рис. 18. Дистанционное управление светодиодами с помощью демоприложения Packet Sender
Приложение для отправки данных по UDP-протоколу
Для того чтобы начать отправлять данные по UDP-протоколу, необходимо выполнить следующие шаги.
- Начать UDP-пакет. Следующая функция позволяет начать формирование пакета данных UDPv4, используя формат протокола IPv4 и заголовок UDP. Поля UDP-заголовка — контрольная сумма и длина поля данных — первоначально выставлены в 0:
- UDP_Start(uint32_t destIP, uint16_t srcPort, uint16_t destPort).
- Записать UDP-пакет. Существует шесть методов записи UDP-пакета, в зависимости от длины пакета и порядка следования данных в нем.
- UDP_WriteBlock(uint8_t* data, uint16_t length) — записать блок данных;
- UDP_Write8(uint8_t data) — записать 1 байт данных;
- UDP_Write16(uint16_t data) — записать 2 байт данных в порядке хоста;
- UDP_Write24(uint32_t data) — записать 3 байт данных в порядке хоста;
- UDP_Write32(uint32_t data) — записать 4 байт данных в порядке хоста;
- UDP_String(const char *string) — записать строку в порядке сети.
- Отправить UDP-пакет. Следующая функция добавит длину поля данных payload в заголовок UDP-пакета, вычислит контрольную сумму, добавит ее к UDP-пакету и отправит пакет в проводную линию связи: UDP_Send().
Приложение для приема данных по UDP-протоколу
Для приема данных по протоколу UDP необходимо выполнить следующие действия.
- Обработка порта. Необходимо обновить содержимое функции UDP_CallBackTable[] (листинг 8), находящейся в файле c. В теле функции содержатся номер порта приема данных (portNumber) и указатель на функцию обратного вызова (&callback).
typedef struct
{
uint16_t portNumber;
ip_receive_function_ptr callBack;
}
udp_handler_t;
const udp_handler_t UDP_CallBackTable [] = \
{
{portNumber, &callback}
};
Листинг 8. Функция UDP_CallBackTable[]
- Прием UDP-пакета: UDP_Receive(uint16_t udpcksm).
Прежде всего происходит проверка контрольной суммы. Если входящий UDP-пакет не проходит эту проверку, он будет проигнорирован. Если контрольная сумма соответствует принятым данным, то принимается решение об их валидности и далее происходит проверка номера порта на соответствие тому значению, которое содержится в функции UDP_CallBackTable[]. Если номер порта и соответствующий заголовок функции обратного вызова (callback) совпадают с данными, приведенными в таблице, то длина поля данных payload передается в качестве параметра этой функции. Любой UDP-пакет, не прошедший проверку по номеру порта, будет сопровождаться ответом ICMP PORT UNREACHABLE, если во время конфигурирования стека в среде MCC была выбрана опция ICMP Port Unreachable. В противном случае пакет будет просто проигнорирован.
- Чтение UDP-пакета. Существует пять методов чтения пакета, в зависимости от его длины и порядка следования данных.
- UDP_ReadBlock(uint8_t* data, uint16_t length) — читать блок данных;
- UDP_Read8() — читать 1 байт данных;
- UDP_Read16() — читать 2 байт данных в порядке хоста;
- UDP_Read24() — читать 3 байт данных в порядке хоста;
- UDP_Read32() — читать 4 байт данных в порядке хоста.
Исходные коды для приложений UDP-сервера/UDP-клиента
Исходные коды UDP-демоприложений (исходники и скомпилированные бинарные файлы) могут быть загружены по адресу www.microchip.com.
Исходный код UDP-клиента приведен в листинге 9. Блок-схема кода изображена на рис. 19.
Рис. 19. Блок-схема демопрограммы для обмена данными по UDP-протоколу
void UDP_DEMO_Send (void)
{
error_msg ret = ERROR;
potCurrResult = (ADCC_GetSingleConversion(Pot)/10);
if(...)
{
...
ret = UDP_Start(udpPacket.destinationAddress, udpPacket.sourcePortNumber, udpPacket.destinationPortNumber);
if(ret = SUCCESS)
{
UDP_Write16(potCurrResult);
UDP_Send();
}
}
}
Листинг 9. Исходный код UDP-клиента
Исходный код UDP-сервера приведен в листинге 10.
Заключение
Изложенный материал представляет собой руководство для очень простого программного решения, использующего возможности TCP-серевера, TCP-клиента, обмена данными по UDP-протоколу. Все описанные возможности основаны на легковесном TCP/IP-стеке компании Microchip, который оптимизирован с точки зрения занимаемого объема памяти, имеет модульную структуру и позволяет создавать эффективные программные решения для систем проводной связи с ограниченными аппаратными ресурсами.
Введение
Тенденция повсеместного внедрения «Интернета вещей» (IoT, или Internet of Things) постепенно набирает обороты. Интенсивно развивающийся рынок требует технологий связи, которые могут быть легко интегрированы в электронные устройства, не обладающие большими вычислительными ресурсами. Сам термин «Интернет вещей» подразумевает добавление функции связи и обработки данных даже в самые простые бытовые приборы. Спектр применений IoT предполагает такие типовые задачи, как дистанционное управление шлагбаумами и автоматическим воротами, бытовой техникой, системами вентиляции и кондиционирования, системами освещения и т. д. Технология связи, используемая для «Интернета вещей», должна быть гибкой, чтобы легко адаптироваться под любую задачу, и нетребовательной к ресурсам микроконтроллера, чтобы иметь возможность эффективно работать даже на самых низкопроизводительных вычислительных системах и оставаться максимально доступной для массового потребителя.
Компания Microchip давно известна на рынке полупроводниковой техники своими 8‑разрядными микроконтроллерами, которые отличаются простотой освоения и достаточно большим набором периферийных расширений, способных работать без участия вычислительного ядра. В сочетании с оригинальными технологиями eXtreme Low Power (XLP) и picoPower для обеспечения сверхнизкого энергопотребления микроконтроллеры семейств PIC и AVR компании Microchip представляют собой хорошую базу для построения систем «Интернета вещей». В широком спектре микроконтроллеров особенно хотелось бы отметить единственный в своем роде 8‑разрядный микроконтроллер со встроенным Ethernet MAC + PHY — PIC18F97J60, на базе которого можно создать недорогое устройство мониторинга и/или управления с подключением в сеть Ethernet.
Особенностью компании Microchip является не только производство полупроводниковых компонентов, но и разработка программно-аппаратных решений для сокращения времени внедрения и выпуска изделий у производителей готовых устройств. Следует отметить программно-аппаратное решение, на базе которого специалисты могут создать простейшие устройства с применением 8‑битного микроконтроллера, соединенные в одну сеть по проводному интерфейсу Ethernet. Данные устройства, используя технологию PoE (Power over Ethernet), легко интегрируются в существующую инфраструктуру сетей Ethernet внутри здания, не требуя дополнительных инвестиций на прокладку новых кабелей. Такой подход значительно упрощает развертывание сетей связи в самом широком диапазоне применений и делает систему передачи данных менее громоздкой.
Наряду с интересными 8‑битными микроконтроллерами большую ценность данного программно-аппаратного решения представляет легковесный, тонко конфигурируемый программный стек протоколов TCP/IP, способный действовать в составе простейшего микроконтроллерного устройства на минимальном объеме памяти, сохраняя при этом вычислительные ресурсы для исполнения пользовательских приложений. О нем и пойдет речь в статье.
Архитектура стека TCP/IP
Применение библиотеки Microchip TCP/IP Lite Stack library основано на модели взаимодействия протоколов TCP/IP, как показано на рис. 1.
Стек TCP/IP разделен на несколько уровней, как можно видеть на рис. 1. Каждый уровень в программной библиотеке Microchip TCP/IP Lite Stack library открыт для прямого доступа с одного или нескольких уровней выше или ниже него.
Работа стека TCP/IP основана на взаимодействии с фоновыми задачами, периодически запускаемыми пользователем и генерирующими различные события — управление тайм-аутами, проверка статуса контроллера Ethernet, разбор принятых данных.
Библиотека включает несколько протоколов, программный код каждого из них расположен в отдельном исходном файле, в то время как API-интерфейс описан в заголовочных файлах и подключаемых модулях.
Конфигурирование стека
Microchip TCP/IP Lite Stack является масштабируемым. То есть пользователь может самостоятельно изменять состав стека в зависимости от требований приложения. Среда разработки MCC предоставляет возможность собирать код из нескольких фрагментов, чтобы добиться необходимой комбинации протоколов для работы в составе приложения пользователя. В среде MCC также можно настраивать параметры выбранных протоколов. Параметры содержатся в конфигурационном файле tcpip_config.h, который является частью среды MCC. Среди наиболее значимых параметров, доступных для конфигурирования, находятся:
- Используется протоколом DHCP и содержит вербальную форму имени, ассоциированную с MAC-адресом. Значение по умолчанию «Имя устройства ETHERNET», например — PIC16F18346 ETHERNET.
- Параметр содержит в себе максимальный размер таблицы APR (Address Resolution Protocol). Значение по умолчанию 8.
- Для случая статического IP-адреса — собственно IP-адрес, маска подсети, адрес шлюза по умолчанию, адреса предпочитаемого и альтернативного серверов DNS.
- Для протокола ICMP — специальная опция для включения автоматического ответа в виде эха и сообщения в случае недоступности порта для связи.
Управление буфером стека TCP/IP
Обзор
По умолчанию стек TCP/IP занимает минимальный возможный объем памяти, чтобы оставить как можно больше памяти для пользовательского приложения.
Для того чтобы максимально использовать возможность оптимального распределения памяти, нужно задействовать все необходимые буферы для обмена данными между TCP/IP-соединениями, как будет описано далее.
Контроллер Ethernet принимает и хранит данные до тех пор, пока стек TCP/IP в состоянии их обработать. Буфер для каждого принятого пакета данных управляется автоматически Ethernet-контроллером, а дескриптор буфера доступен для редактирования пользователем.
Ethernet-контроллер начинает автоматически удалять ранее принятые, не обработанные стеком данные из буфера, если памяти буфера становится недостаточно для хранения вновь поступающих пакетов.
Пакеты данных Ethernet, подготовленные для передачи, также помещаются в специально отведенную память контроллера.
Буферы, используемые протоколом UDP
Стек позволяет напрямую передавать пакеты payload в RAM-память Ethernet-контроллера. Для пользователя доступны API-функции по управлению формированием payload-пакетов UDP, их передачей между областями памяти и их отправкой в среду передачи.
Когда данные поступают по протоколу UDP, Ethernet-контроллер автоматически помещает их в буфер. Если пакеты успешно приняты и зарегистрирован вызов от пользователя для входящего порта, стек запускает назначенную пользователем функцию и предоставляет возможность прямого доступа к payload-данным прямо в памяти Ethernet-контроллера. Такой подход исключает необходимость многократного копирования и сохранения payload-данных, позволяет экономить время и память контроллера.
Буферы, используемые протоколами TCP/IP
В случае применения протокола TCP пользователь должен специально отвести определенное количество памяти для каждого TCP-соединения. Есть несколько типов буферов, используемых TCP-протоколом:
Память сокета (socket memory). Память, отведенная для каждого сокета, распределяется пользователем. Задача управления памятью решается с помощью специального набора API-функций. Такой подход актуален для тех случаев, когда информация о всех TCP-соединениях полностью известна.
Буферы приема (Rx) и передачи (Tx). Оба вида буферов для каждого TCP-соединения должны быть созданы пользователем и переданы стеку с помощью специальных API-функций. Каждый сокет может иметь единовременно только один буфер приема (Rx) и один — передачи (Tx). Стек всегда должен иметь свободный буфер Rx для принятия данных от удаленного хоста. Стек способен функционировать без Rx-буфера в течение очень ограниченного интервала времени перед тем, как поступит запрос на повторную передачу пакета.
Особенности и ограничения стека TCP/IP
Стек TCP/IP имеет некоторые ограничения, связанные с небольшим объемом памяти RAM и Flash, доступной для функционирования малопроизводительных устройств, таких как 8‑битные микроконтроллеры. Чтобы получить исчерпывающую информацию обо всех актуальных особенностях и ограничениях, следует обратиться к соответствующим документам release notes, относящимся к среде MCC и библиотеке TCP/IP Lite library.
Запуск демоприложений со стеком TCP/IP
Перечень необходимого программного и аппаратного обеспечения для запуска демоприложений:
- Отладочная плата Curiosity Development Board (DM164137).
- Микроконтроллер PIC16F18346.
- Доступное питание от USB-порта.
- Среда разработки программного обеспечения MPLAB X v3.45 или более новая.
- Компилятор языка Си XC8 v1.41 или более новый.
- Персональный компьютер с операционной системой Windows, Linux или MAC.
- Набор демоприложений TCP/IP.
- Интерфейсная плата Ethernet ENC28J60 Click Board (ETH Click).
- Программное приложение Packet Sender для отправки пакетов TCP или UDP. Приложение может быть загружено по интернет-ссылке https://packetsender.com/.
- Сервер DHCP (без него отладочная плата не сможет получить IP-адрес, и демоприложение UDP не сможет работать). Демоприложение TCP работает только со статическим IP-адресом.
- Ethernet-кабели:
- сквозной — для подключения к роутеру или коммутатору;
- кросс-кабель — для прямого подключения к компьютеру.
Настройки аппаратуры
- Подключаем плату ETH Click к плате Curiosity Development board через коннектор J35.
- Подключаем ENC28J60 к сети Ethernet с помощью соответствующего кабеля. Плата должна иметь возможность подсоединяться к работающему DHCP-серверу для запуска UDP-демоприложения.
- Подключаем питание от USB-порта к плате Curiosity Development Board, используя коннектор J2.
- Для успешного запуска каждого приложения пользователь должен выполнять определенную последовательность действий, кратко описанных далее и более подробно рассмотренных в соответствующем руководстве к данному приложению.
Установка демоприложений для TCP-клиента/сервера с использованием среды MCC
Для создания и запуска приложения, использующего TCP, пользователь должен настроить программный модуль TCP/IP Lite, доступный в среде MCC, как показано далее:
- Запускаем приложение MPLAB X и создаем новый проект для устройства PIC16F18346. Данный демопроект использует PIC16F18346, но его можно легко портировать на любой другой 8‑битный микроконтроллер.
- Открываем среду MCC.
- В разделе Device Resources → Libraries двойным щелчком мыши выбираем модуль TCP/IP Lite, как показано на рис. 2.
- Демоприложение TCP требует некоторых изменений в модуле TCP/IP, как показано на рис. 3:
- UDP — не отмечен;
- DHCP — не отмечен. Пользователь должен предоставить собственную конфигурацию со статическим IP-адресом;
- TCP — отмечен.
- Вкладка Notifications показывает различные типы сообщений (рис. 4):
- WARNING — требует исправить некоторую ошибку внутри кода;
- HINT — помогает пользователю отслеживать процесс генерирования кода;
- INFO — дает пользователю информацию о загруженных модулях.
Вкладка Notifications показывает все особенности в процессе формирования конечного кода.
- Стек TCP/IP Lite требует использования модуля Ethernet MAC и модуля Timer1 (рис. 5):
в разделе Device Resources → Libraries → Ethernet, двойной щелчок по иконке MAC;
в разделе Device Resources → Peripherals → Timer, двойной щелчок по иконке TMR1.
- Устройство должно быть сконфигурировано на использование системных часов с секундной меткой. Пользователю следует настроить часы Timer1, как показано на рис. 6:
- Timer Period = 250 ms;
- Enable Timer Interrupt = Отмечено;
- Callback Function Rate = 4.
- Далее приведены основные шаги для конфигурирования модуля Ethernet MAC (рис. 7). В окне Easy Setup пользователю следует выбрать тип контроллера ENC28J60 в выпадающем списке. Контроллер ENC28J60 использует интерфейс SPI. Отладочная плата Curiosity Development board (DM164137) поддерживает интерфейс MSSP1 — SPI, выведенный на коннекторе J35.
- Пользователю следует выбрать модуль MSSP1 в списке, как показано на рис. 8:
- в разделе Device Resources → Peripherals → MSSP, двойной щелчок мыши по иконке MSSP1.
- Шаги, необходимые для настройки SPI-модуля MSSP1, показаны на рис. 9:
- Mode = SPI Master;
- Input Data Sampled at = End;
- Clock Edge = Active to Idle.
- Далее следует настроить модуль ADC (рис. 10) для передачи показаний потенциометра через TCP-соединение:
- в разделе Device Resources → Peripherals → ADC, двойной щелчок по иконке ADC.
- Процесс настройки модуля ADC проиллюстрирован на рис. 11:
- Clock Source = FOSC/4;
- Result Alignment = Right.
- Теперь необходимо настроить назначение выводов с помощью раздела меню Pin Module (рис. 12):
- Настройка выводов MSSP1:
- SCK1 — output Port RB6;
- SDI1 — input Port RB4;
- SDO1 — output Port RC7.
- Настройка выводов модуля MAC:
- ETH_CS — для контроллера ENC28J60 — output Port RC6.
- Настройка выводов модуля ADC:
- ANx — input Port RC0;
- в разделе Project Resources → System → Pin Module пользователь может задать любое имя Custom Name для канала AN0, например Pot.
- Настройка интерфейсов LED на плате Curiosity board:
- Pin module — output Port RA5;
- вразделе Project Resources → System → Pin Module пользователь может задать имя Custom Name для вывода RA5, например
- Теперь все пользовательские настройки заданы, и можно нажать кнопку Generate.
Простое демоприложение TCP-клиента
Обзор
Простое демоприложение, реализующее функции TCP-клиента, подключающегося к серверу, работающему на порте № 60 персонального компьютера. Пользователю следует задать IP-адрес в прошивке. Как только соединение клиента с сервером установлено, клиент будет автоматически посылать на сервер пакеты своего статуса с периодом в 2 с. Пакеты, посылаемые клиентом, будут содержать значение потенциометра и состояние LED-индикаторов. С помощью сервера пользователь может управлять LED-светодиодами на плате (вкл./выкл.), используя программные GUI-кнопки. В демоприложении каждая GUI-кнопка связана с соответствующим светодиодом.
В демоприложении используется только одно активное TCP-под-ключение. На самом деле стек TCP/IP поддерживает множественные подключения через TCP-протокол на одной плате. Для каждого нового подключения пользователь должен создать отдельный сокет, буфер приема Rx и попытаться подключиться к серверу. Демоприложение настроено для запуска на отладочной плате Curiosity board.
Демо-TCP-клиент будет автоматически пытаться подключиться к серверу каждые 2 с. Это сделано, чтобы пользователю было проще перехватывать и анализировать пакеты с помощью приложения Wireshark protocol analyzer.
Программные настройки для демо-TCP-клиента
- Прежде всего пользователю следует определить IP-адрес компьютера, на котором запущено демоприложение Java.
- Далее используем TCP/IP Lite library из созданного ранее проекта.
- Добавляем в новый проект файлы tcp_client_demo.c и tcp_client_demo.h.
- Внутри функции main() основного программного модуля проекта следует вызвать функцию TCP_Client_Initialize() для добавления IP-адреса и номера порта. Пример конфигурирования IP-адреса и номера порта приведен в листинге 1.
remoteSocket.addr.s_addr = MAKE_IPV4_ADDRESS(192,168,0,3);
remoteSocket.port = 60;
Листинг 1. Пример конфигурирования IP-адреса и номера порта
- В функции main() пользователь должен задать глобальные и периферийные прерывания.
- В цикле while(1) следует вызывать команду Network_Manage(), которая периодически опрашивает Ethernet-контроллер на наличие пакетов данных и обрабатывает их.
- В цикле while(1) следует вызывать команду DEMO_TCP_Client(), которая управляет состоянием сокета, через который клиент подключен к серверу.
- Проект должен быть скомпилирован с использованием компилятора XC8.
- С помощью среды MPLAB X пользователь может загрузить скомпилированную прошивку в плату Curiosity Development board, построенную на базе PIC16F18346.
- Если в среде MCC задан IP-адрес платы, валидный IP-адрес компьютера, на котором работает TCP/IP-сервер, оба IP-адреса уникальны и находятся в одной сети, то становится возможным обмен пакетами данных. Пользователь может проверить наличие связи с использованием сообщений ICMP (с помощью команды пингования).
- Теперь можно запустить демонстрационное Java-приложение на ПК для начала тестирования возможностей стека TCP/IP, как показано на рис. 13.
- Переходим ко вкладке TCP Server Demo.
- В поле Server → Local Port изменяем номер порта на 60.
- Нажимаем кнопку Listen (статус приложения изменяется на Listen, а надпись на кнопке изменяется на Disconnect).
- Когда плата Curiosity board подключится к компьютеру, в поле Sent/Received Data появится сообщение, приведенное в листинге 2.
192.168.0.21: Connected
Листинг 2. Сообщение в поле Sent/Received Data
- В поле Send пользователь может набирать текстовые сообщения для отправки на плату. Сообщение будет отправлено, когда пользователь нажмет клавишу Enter или кнопку Send в окне программы. Отправленные и полученные данные отображаются разными цветами в поле Sent/Received Data.
- Сообщения, приходящие от платы, автоматически выводятся в окно Sent/Received Data и содержат информацию о состоянии светодиодов на плате и строку со значением встроенного потенциометра. Все данные приведены в 16‑ричном формате.
- Нажатие программных кнопок LED 0–LED 7 инициирует отправку на плату команд управления светодиодами. Каждая кнопка включает одноименный светодиод. Единовременно можно включать или отключать только один светодиод.
- Нажатие программной кнопки Disconnect приведет к закрытию соединения с сервером и появлению сообщения Server closed вслед за сообщением Client disconnected. Отдельное сообщение будет выведено для каждого сообщения Client connected.
- Шаги 13–18 могут быть использованы для тестирования возможностей соединения.
Прошивка для демоприложения TCP-клиента: создание буфера
Прежде чем начинать работу с клиентской станцией, пользователь должен создать по меньшей мере сокет и приемный Rx-буфер, как показано в листинге 3.
// create the socket for the TCP Client
tcpTCB_t port60TCB;
// create the TX and RX buffers
uint8_t rxdataPort60[50];
uint8_t txdataPort60[80];
Листинг 3. Сокет и приемный Rx-буфер
Буфер передачи Tx должен создаваться и передаваться TCP-стеку пользователем в момент готовности данных к передаче.
Применение TCP-клиента
Для создания и применения TCP-клиента требуются следующие шаги.
- Инициализация TCP-стека. Функция инициализации TCP_Init() должна быть вызвана перед тем, как начнут использоваться все остальные функции TCP-стека. Обычно инициализация выполняется в автоматическом режиме с помощью функции Network_Init(), расположенной внутри функции SYSTEM_Initialize().
- Установка IP-адреса и номера порта. Пользователь должен указать номер порта, к которому собирается подключаться (это номер порта, который будет прослушивать сервер на наличие передаваемых данных), и IP-адрес компьютера, на котором запущено Java-приложение TCP/IP-сервера. Функция задания номера порта и IP-адреса должна быть вызвана перед началом цикла while(1):
TCP_Client_Initialize();
- Проверка статуса сокета:
socket_state = TCP_SocketPoll(&port7TCB);
Функция проверяет, что указатель, переданный в качестве параметра, зарегистрирован внутри TCP/IP-стека как сокет. Если указатель является валидным сокетом, то функция возвращает его статус. Возможные состояния статуса перечислены в заголовочном файле tcpv4.h.
- Инициализация сокета с целью создания подключения. Вся необходимая информация содержится внутри данной функции:
TCP_SocketInit(&port60TCB);
- Задание локального порта для клиента. Этот шаг необязателен, поскольку TCP-стек автоматически использует ближайший доступный номер порта в качестве локального порта. Пользователь должен использовать функцию TCP_Bind(), чтобы узнать, какой именно номер порта был выбран стеком после инициализации соединения. Этот прием может быть полезен только в случае автоматического выбора номера порта стеком:
TCP_Bind(&port60TCB, 1024);
- Добавление приемного Rx-буфера для активированного сокета. Следующая функция автоматически привяжет буфер к сокету. Буфер будет использоваться для хранения поступающих данных:
TCP_InsertRxBuffer(&port60TCB, rxdat aPort60, sizeof(rxdataPort60));
- Включение TCP/IP-клиента с помощью функции TCP_Connect(), которая запустит процедуру подключения к серверу. Если установка соединения пройдет успешно, пользователь получит возможность обмена данными с сервером через TCP-соединение:
TCP_Connect(&port60TCB, &remoteSocket);
- Закрыть TCP-соединение. Если соединение не удалось или прекратилось в аварийном режиме, то пользователь должен принудительно выполнить процедуру его завершения и закрыть сокет. Только после этого можно пытаться инициировать новое TCP-соединение с интервалами между попытками не менее 2 с.
Следующая функция автоматически закроет неудавшееся или аварийное TCP-подключение. Параллельно пользователь должен периодически проверять состояние TCP-подключения до тех пор, пока он не убедится в его закрытии. После успешного закрытия соединения приемный Rх- и передающий Tx-буферы могут быть безопасно использованы повторно:
TCP_Close(&port60TCB);
- Проверка корректности передачи содержимого буфера Tx. Данная функция должна обязательно вызываться перед тем, как послать в буфер новую порцию данных, потому что сокет может оперировать единовременно только одним буфером:
TCP_SendDone(&port60TCB);
- Чтение принятых данных из Rx-буфера и контроль доступности буфера для использования. Следующая функция возвращает количество байт, имеющихся в Rx-буфере для чтения. Сразу после вызова данной функции буфер перестанет быть доступным для хранения вновь поступающих данных. Поэтому для сохранения новых данных и избегания повторных передач пользователю следует создать новый буфер и как можно быстрее передать его в стек:
rxLen = TCP_GetReceivedData(&port60TCB);
- Отправка содержимого Tx-буфера удаленному серверу. Интерфейс API позволяет пользователю производить отправку данных через активное TCP-соединение. Данные не смогут быть переданы, пока соединение между клиентом и сервером не установлено:
TCP_Send(&port60TCB, txdataPort60, txLen);
- Удаление сокета. Если сокет закрыт и пользователь намерен удалить его из внутреннего списка сокетов, он может использовать следующую API-функцию:
TCP_SocketRemove(&port60TCB);
- Фоновая задача. Следующая функция должна периодически вызываться, чтобы перехватывать тайм-ауты от TCP-стека:
TCP_Update(); // handle timeouts.
Исходный код приложения
для TCP-клиента
Демокод приложения TCP-клиента (исходный код и скомпилированный бинарный файл) доступен для загрузки с сайта www.microchip.com. Исходный код приведен в листинге 4. Блок-схема кода показана на рис. 14.
void DEMO_TCP_Client(void)
{
// create the socket for the TCP Client
static tcpTCB_t port60TCB;
// create the TX and RX Client’s buffers
static uint8_t rxdataPort60[50];
static uint8_t txdataPort60[80];
static time_t t_client = 0;
static time_t socketTimeout = 0;
uint16_t rx_len;
sockaddr_in_t remoteSocket;
socketState_t socketState;
rx_len = 0;
socketState = TCP_SocketPoll(&port60TCB);
time(&t_client);
switch(socketState)
{
case NOT_A_SOCKET:
// Inserting and initializing the socket
TCP_SocketInit(&port60TCB);
break;
case SOCKET_CLOSED:
// if the socket is closed we will try to connect again
// try to connect once at 2 seconds
socketTimeout = t_client + 2;
TCP_InsertRxBuffer(&port60TCB,rxdataPort60, sizeof(rxdataPort60));
TCP_Connect(&port60TCB, &remoteSocket);
break;
case SOCKET_IN_PROGRESS:
// close the socket
if(t_client >= socketTimeout)
{
TCP_Close(&port60TCB);
}
break;
case SOCKET_CONNECTED:
// implement an echo client over TCP
// check if the previous buffer was sent
if (TCP_SendDone(&port60TCB))
{
rx_len = TCP_GetReceivedData(&port60TCB);
// handle the incoming data
if(rx_len > 0)
{
/* …………………………………………………………………….
LED Command parsing and LCD updates was removed from this example.
The full code is available in the source code.
…………………………………………………………………….
*/
// reuse the RX buffer
TCP_InsertRxBuffer(&port60TCB, rxdataPort60, sizeof(rxdataPort60));
}
if(t_client >= socketTimeout)
{
// send board status message only once at 2 seconds
socketTimeout = t_client + 2;
/*……………………………………………………………………….
Composing the TX message in the TX buffer was removed from this example.
The full code is available in the source code.
………………………………………………………………………….
*/
//send data back to the source
TCP_Send(&port60TCB, txdataPort60, strlen(txdataPort60));
}
}
break;
case SOCKET_CLOSING:
//remove the used socket form the list
TCP_SocketRemove(&port60TCB);
break;
default:
break;
}
}
Листинг 4. Исходный код для приложения TCP-клиента
Демоприложение TCP-сервера
Обзор
Простое приложение TCP — эхо сервера, прослушивающего данные на порте № 7. Сервер запускается на отладочной плате Curiosity board и ожидает любых входящих подключений. Сервер возвращает эхо-ответ для всех входящих данных сразу, как только устанавливается соединение с клиентом. Для данного демоприложения возможна единовременная установка только одного активного соединения. Однако стек TCP/IP поддерживает возможность множественных подключений на одной отладочной плате. Пользователь должен создавать новый сервер (создание Rх/Tx-буферов, инициализация и запуск прослушивания данных) для каждого нового соединения.
Настройки программного демоприложения TCP-сервера
- Пользователь должен создать проект, как это было показано в разделе «Установка демоприложений для TCP-клиента/сервера с использованием среды MCC», исключив шаги 11 и 12. Серверу не требуется задействование модулей АЦП и блоков LED-индикаторов.
- Добавляем файлы c и tcp_server_demo.h к созданному проекту.
- Внутри функции mail() пользователю необходимо активировать глобальные и периферийные прерывания.
- В цикле while(1) должна вызываться API-функция Network_Manage(), которая опрашивает Ethernet-контроллер, перехватывает и обрабатывает пакеты данных.
- В цикле while(1) должна вызываться API-функция DEMO_TCP_EchoServer() для открытия сокета и прослушивания входящих клиентских подключений.
- Проект следует компилировать с помощью XC8.
- Используя среду MPLAB X, пользователь может запрограммировать прошивку в микроконтроллер PIC16F18346 на плате Curiosity Development board.
- Запускаем TCP/IP Java-демоприложение на компьютере, как показано на рис. 15.
- Переходим на вкладку TCP Client Demo в окне приложения.
- Редактируем окно Server IP Address и устанавливаем значение IP-адреса для порта № 7. IP-адрес является статическим и может быть изменен также через среду MCC.
- Нажимаем программную кнопку Connect.
- Когда компьютер подключится к плате Curiosity board, в окне Sent/Received Data появится сообщение, как показано в листинге 5.
Connected to 192.168.0.21 Port: 7
Листинг 5. Сообщение о подключении к плате Curiosity board
- Далее пользователь может печатать любые данные в окне Send и отправлять их нажатием клавиши Enter или програм-мной кнопки Send. Отправленные и полученные данные будут отображаться разными цветами в окне Sent/Received Data.
- Нажатие программной кнопки Disconnect приведет к закрытию TCP-соединения. Сообщение Connection closed будет выведено по завершении TCP-соединения.
- Можно повторить шаги 9–13 для тестирования TCP-соединения при отправке строк данных разной длины.
- Чтобы сгенерировать трафик TCP-соеди-нения, следует нажать кнопку ECHO back received message. Поле Send должно быть предварительно заполнено данными, которые предполагается отправлять на отладочную плату. Нажатие программной кнопки Send инициирует обмен данными. Остановить трафик обмена данными можно повторным нажатием кнопки Echo Back received message. В этом случае в поле Sent/Received Data будут отображаться только принятые данные.
Демопрошивка TCP-сервера: создание буфера
Перед запуском сервера пользователь должен создать по меньшей мере сокет и буфер приема Rx. Буфер передачи пользователю надо создать и передать TCP-стеку, когда появятся данные, готовые для передачи.
Применение TCP-сервера
Далее приведены шаги, необходимые для запуска и применения TCP-сервера.
- Инициализация TCP-стека. Данная функция должна быть вызвана перед вызовом любых других TCP-функций:
TCP_Init();
Инициализация выполняется автоматически функцией Network_Init(), вызываемой внутри функции SYSTEM_Initialize() среды MCC.
- Проверка статуса сокета. Функция проверяет указатель, передаваемый в качестве параметра, на предмет его регистрации внутри TCP-стека в качестве сокета: socket_state =
TCP_SocketPoll(&port7TCB);
Если сокет является валидным, то функция возвращает его статус. Возможные значения статуса приведены в заголовочном файле tcpv4.h.
- Передача и инициализация сокета с целью создания подключения. Вся необходимая информация для TCP-подключения содержится в данной функции:
TCP_SocketInit(&port7TCB);
- Связывание номера локального порта с сервером. Данная функция связывает локальный порт с прослушиваемым сокетом:
TCP_Bind(&port7TCB, 7);
Сервер будет прослушивать входящие соединения на данном порте. TCP-стек автоматически выделит номер порта, если он не предоставлен пользователем.
- Добавление приемного буфера Rx к сокету. Следующая функция привяжет буфер к сокету для хранения принимаемых данных:
TCP_InsertRxBuffer(&port7TCB, rxdataPort7, sizeof(rxdataPort7));
- Запуск TCP-сервера. Данная функция заставляет TCP-стек прослушивать порт на запросы входящих подключений:
TCP_Listen(&port7TCB);
Если TCP-соединение успешно устанавливается, то пользователь получает возможность обмениваться данными с удаленным узлом. Только один запрос на подключение может быть принят единовременно в рамках действующего демоприложения. Однако TCP-стек способен работать с множественными подключениями для одного порта. Но для каждого подключения пользователь должен создавать отдельный сокет, Rx-буфер и экземпляр TCP-сервера на том же порте.
- Проверка статуса сокета. Данная функция проверяет, является ли указатель, переданный в качестве параметра, сокетом, зарегистрированным TCP/IP-стеком:
socket_state = TCP_SocketPoll(&port7TCB);
Если сокет является валидным, то функция возвращает его статус. Возможные значения статуса приведены в заголовочном файле tcpv4.h.
- Проверка корректности отправки содержимого буфера передачи Tx (это означает, что удаленный узел отправил подтверждение полученных данных):
TCP_SendDone(&port7TCB);
Эта функция должна вызываться каждый раз перед попыткой отправить что-либо, потому что сокет способен оперировать только одним буфером в каждый момент времени.
- Проверка новых данных в буфере приема Rx. Данная функция возвращает количество байт, содержащихся в Rx-буфере:
rxLen = TCP_GetRxLength(&port7TCB);
- Подсчет числа байт данных, доступных для чтения в Rx-буфере, и подготовка буфера для использования. Следующая функция возвращает количество байт данных, доступных в буфере:
rxLen = TCP_GetReceivedData (&port7TCB);
После вызова данной функции пользователь может получить безопасный доступ к буферу. Сразу после вызова данной функции буфер становится недоступен для принятия вновь поступающих данных. Чтобы избежать повторов передачи от удаленного узла, пользователь должен предоставить стеку новый свободный буфер.
- Отправка содержимого буфера передачи удаленному Tx-узлу. API-функции позволяют пользователю осуществлять передачу данных через активное TCP-соеди-нение:
TCP_Send(&port7TCB, txdataPort7, txLen);
Невозможно передать данные, если предварительно не было установлено активное соединение между локальной машиной и удаленным хостом.
- Закрытие TCP-соединения:
TCP_Close(&port7TCB)
Закрытие соединения произойдет только после обмена дружественным рукопожатием между участниками соединения. Пользователю следует периодически проверять состояние сокета до тех пор, пока сокет не окажется закрыт. После закрытия сокета буферы приема и передачи могут быть безопасно использованы снова.
- Удаление сокета. Если после закрытия сокета пользователь желает удалить его из внутреннего списка сокетов, то он должен использовать следующую функцию:
TCP_SocketRemove(&port7TCB)
- Фоновая задача. Данная функция должна периодически запускаться приложением, чтобы перехватывать тайм-ауты от TCP-стека. Запуск функции должен происходить один раз в секунду.
TCP_Update();// handle timeouts
Исходный код приложения TCP-сервера
Демокод TCP-сервера (исходный код и откомпилированный бинарный файл) доступен для загрузки по адресу www.microchip.com. Исходный код приложения приведен в листинге 7. Блок-схема кода показана на рис. 16.
void DEMO_TCP_echo_server(void)
{
// create the socket for the TCP Server static tcpTCB_t port7TCB;
// create the TX and RX buffers static uint8_t rxdataPort7[20];
static uint8_t txdataPort7[20];
uint16_t rxLen, txLen, i;
socket_state_t socket_state;
rxLen = 0;
// checking the status of the socket
socket_state = TCP_SocketPoll(&port7TCB);
switch(socket_state)
{
case NOT_A_SOCKET:
//Inserting and initializing the socket
TCP_SocketInit(&port7TCB);
case SOCKET_CLOSED:
//configure the local port
TCP_Bind(&port7TCB, 7);
// add receive buffer
TCP_InsertRxBuffer(&port7TCB,rxdataPort7, sizeof(rxdataPort7));
// start the server
TCP_Listen(&port7TCB);
break;
case SOCKET_CONNECTED:
// check if the buffer was sent, if yes we can reuse the
//buffer
if(TCP_SendDone(&port7TCB))
{
// check to see if there are any received data
rxLen = TCP_GetRxLength(&port7TCB);
if(rxLen > 0)
{
//make sure it safe to use the receive buffer
rxLen = TCP_GetReceivedData(&port7TCB);
//simulate some buffer processing copy from
//the RX buffer to the TX buffer
for(i = 0; i < rxLen; i++)
{
txdataPort7[i] = rxdataPort7[i];
}
// reuse the rx buffer
TCP_InsertRxBuffer(&port7TCB,rxdataPort7, sizeof(rxdataPort7));
txLen = rxLen;
//send data back to the source
TCP_Send(&port7TCB, txdataPort7, txLen);
}
}
break;
case SOCKET_CLOSING:
TCP_SocketRemove(&port60TCB);
break;
default:
// we should not end up here
break;
}
}
Листинг 7. Исходный код приложения TCP-сервера
Демоприложение протокола UDP
Обзор
В данном примере рассмотрено применение клиента и сервера, работающих по протоколу UDP. Пример состоит из части UDP Send (UDP Client) и UDP Receive (UDP Server). В качестве части UDP Send отладочная плата Curiosity Development отправляет значения встроенного потенциометра в пакетах UDP-протокола. В качестве части UDP Receive отладочная плата Curiosity Development board слушает входящие UDP-подсоединения, включая команды управления светодиодными LED-индикаторами, на порте № 65531. Номер порта может быть любым в диапазоне 0–65535. Но некоторые номера зарезервированы или уже зарегистрированы для использования по умолчанию. Поэтому рекомендуется выбирать номер порта в диапазоне 49152–65535, поскольку все они гарантированно свободны.
Установка программного обеспечения для демоприложений UDP Send/UDP Receive:
- Запускаем MPLAB X и создаем новый проект для устройства PIC16F18346.
- Загружаем модуль MCC.
- Заходим в раздел Device Resources и в подразделе Libraries выбираем пункт TCP/IP Lite module.
- Загружаем TCP/IP Lite module.
- Вкладка Notifications содержит различные виды сообщений:
- WARNING — означает присутствие явных ошибок внутри кода;
- HINT — отражает ход генерации программного кода;
- INFO — содержит информацию о загружаемом модуле.
- Идем в раздел Device Resources и в подразделе Libraries выбираем Ethernet → MAC module.
- Загружаем MAC module.
- В разделе Easy Setup в выпадающем списке выбираем контроллер ENC28J60.
- Контроллер ENC28J60 содержит встроенный интерфейс SPI. Следовательно, согласно схеме платы Curiosity Development board интерфейс MSSP1 — SPI выведен на коннектор J35.
- Заходим в раздел Device Resources → Peripherals → MSSP и двойным щелчком мыши активируем интерфейс MSSP1.
- Загружается программный модуль MSSP1. В модуле следует установить параметр Mode = SPI Master, параметр Clock Edge = Active to Idle, параметр Input Data Sampled = End.
- Заходим в раздел Device Resources → Peri-pherals → Timer и выбираем пункт TMR1.
- В загрузившемся модуле TMR1 устанавливаем Timer Period = 250 ms. Активируем Timer Interrupt и устанавливаем Callback Function Rate = 4, чтобы получить Timer Period = 1 с. Это необходимо, чтобы синхронизировать работу устройства с секундной меткой времени.
- Аналогичным образом необходимо загрузить и сконфигурировать программный модуль ADC, чтобы отправлять данные потенциометра через UDP-соединение. Заходим в раздел Device Resources → Peripherals → Двойным щелчком мыши по значку ADC загружаем одноименный модуль.
- Вмодуле ADC устанавливаем Clock Source = FOSC/4 и Result Alignment = right.
- Конфигурируем менеджер выводов Pin Manager:
- Конфигурация выводов MSSP1:
– SCK1 — output Port RB6;
– SDI1 — input Port RB4;
– SDO1 — output Port RC7.
- Конфигурация выводов MAC:
– ETH_CS (for the ENC28J60 control-ler) — output Port RC6.
- Конфигурация выводов ADC:
– ANx — input PORT RC0;
– в разделе Project Resource → System → Pin Module пользователь может задать произвольное имя для канала AN0, например Pot.
- Настройка выводов LED-индикатора для платы Curiosity board:
– Pin module — output Port RA5;
– в разделе Project Resource → System → Pin Module пользователь может задать произвольное имя для вывода RA5, например Toggle_Led.
- Нажимаем программную кнопку Generate, чтобы запустить процесс генерирования кода программы.
- Добавляем в проект файлы c и udp_demo.h.
- В функции main() пользователь должен активировать глобальные и периферийные прерывания.
- В функции main() вызываем API-функ-цию POT_UDP_Initialize(), находящуюся в файле c, для инициализации потенциометра и пакетов UDP-сервера: UDP server (destination) Port Number = 65531, UDP Server Address = IP-адрес компьютера (Destination address), UDP Client (Source) Port Number = 65533.
- Внутри цикла while (1) вызываем API-функцию Network_Mange(), чтобы опрашивать Ethernet-контроллер на появление новых пакетов данных и обрабатывать их.
- Проект демоприложения должен быть скомпилирован в среде XC8 для контроллера PIC16F18346, установленного на плате Curiosity Development board.
- Запускаем приложение Packet Sender на компьютере, как показано на рис. 17.
- Заходим в меню File → Settings → Network, активируем параметр UDP Server и выставляем номер порта 65531.
- Поворачиваем ручку на плате Curiosity Development board. Плата автоматически посылает пакеты со значением потенциометра в вольтах.
- В приложении Packet Sender можно отправлять UDP-пакеты для управления светодиодными LED-индикаторами на плате Curiosity Development board (рис. 18):
- Name = UDP Led1 Send;
- ASCII = L1;
- Address = IP-адрес платы. Адрес платы можно посмотреть спомощью утилиты Wireshark analyzer tool в разделе D‑Discover → O‑Offer → RRequest → A‑Acknowledgment;
- Port = 65531;
- выбрать протокол UDP;
- нажать программную кнопку Save для сохранения пакета.
- Нажимаем программную кнопку Send для отправки сохраненного UDP-пакета плате Curiosity Development board и наблюдаем работу светодиодных индикаторов на ней.
- Повторяем шаги 25 и 27, чтобы убедиться, что между компьютером и отладочной платой происходит обмен данными по UDP-протоколу через порт 65531.
Приложение для отправки данных по UDP-протоколу
Для того чтобы начать отправлять данные по UDP-протоколу, необходимо выполнить следующие шаги.
- Начать UDP-пакет. Следующая функция позволяет начать формирование пакета данных UDPv4, используя формат протокола IPv4 и заголовок UDP. Поля UDP-заголовка — контрольная сумма и длина поля данных — первоначально выставлены в 0:
- UDP_Start(uint32_t destIP, uint16_t srcPort, uint16_t destPort).
- Записать UDP-пакет. Существует шесть методов записи UDP-пакета, в зависимости от длины пакета и порядка следования данных в нем.
- UDP_WriteBlock(uint8_t* data, uint16_t length) — записать блок данных;
- UDP_Write8(uint8_t data) — записать 1 байт данных;
- UDP_Write16(uint16_t data) — записать 2 байт данных в порядке хоста;
- UDP_Write24(uint32_t data) — записать 3 байт данных в порядке хоста;
- UDP_Write32(uint32_t data) — записать 4 байт данных в порядке хоста;
- UDP_String(const char *string) — записать строку в порядке сети.
- Отправить UDP-пакет. Следующая функция добавит длину поля данных payload в заголовок UDP-пакета, вычислит контрольную сумму, добавит ее к UDP-пакету и отправит пакет в проводную линию связи: UDP_Send().
Приложение для приема данных по UDP-протоколу
Для приема данных по протоколу UDP необходимо выполнить следующие действия.
- Обработка порта. Необходимо обновить содержимое функции UDP_CallBackTable[] (листинг 8), находящейся в файле c. В теле функции содержатся номер порта приема данных (portNumber) и указатель на функцию обратного вызова (&callback).
typedef struct
{
uint16_t portNumber;
ip_receive_function_ptr callBack;
}
udp_handler_t;
const udp_handler_t UDP_CallBackTable [] = \
{
{portNumber, &callback}
};
Листинг 8. Функция UDP_CallBackTable[]
- Прием UDP-пакета: UDP_Receive(uint16_t udpcksm).
Прежде всего происходит проверка контрольной суммы. Если входящий UDP-пакет не проходит эту проверку, он будет проигнорирован. Если контрольная сумма соответствует принятым данным, то принимается решение об их валидности и далее происходит проверка номера порта на соответствие тому значению, которое содержится в функции UDP_CallBackTable[]. Если номер порта и соответствующий заголовок функции обратного вызова (callback) совпадают с данными, приведенными в таблице, то длина поля данных payload передается в качестве параметра этой функции. Любой UDP-пакет, не прошедший проверку по номеру порта, будет сопровождаться ответом ICMP PORT UNREACHABLE, если во время конфигурирования стека в среде MCC была выбрана опция ICMP Port Unreachable. В противном случае пакет будет просто проигнорирован.
- Чтение UDP-пакета. Существует пять методов чтения пакета, в зависимости от его длины и порядка следования данных.
- UDP_ReadBlock(uint8_t* data, uint16_t length) — читать блок данных;
- UDP_Read8() — читать 1 байт данных;
- UDP_Read16() — читать 2 байт данных в порядке хоста;
- UDP_Read24() — читать 3 байт данных в порядке хоста;
- UDP_Read32() — читать 4 байт данных в порядке хоста.
Исходные коды для приложений UDP-сервера/UDP-клиента
Исходные коды UDP-демоприложений (исходники и скомпилированные бинарные файлы) могут быть загружены по адресу www.microchip.com.
Исходный код UDP-клиента приведен в листинге 9. Блок-схема кода изображена на рис. 19.
void UDP_DEMO_Send (void)
{
error_msg ret = ERROR;
potCurrResult = (ADCC_GetSingleConversion(Pot)/10);
if(…)
{
…
ret = UDP_Start(udpPacket.destinationAddress, udpPacket.sourcePortNumber, udpPacket.destinationPortNumber);
if(ret = SUCCESS)
{
UDP_Write16(potCurrResult);
UDP_Send();
}
}
}
Листинг 9. Исходный код UDP-клиента
Исходный код UDP-сервера приведен в листинге 10.
Заключение
Изложенный материал представляет собой руководство для очень простого программного решения, использующего возможности TCP-серевера, TCP-клиента, обмена данными по UDP-протоколу. Все описанные возможности основаны на легковесном TCP/IP-стеке компании Microchip, который оптимизирован с точки зрения занимаемого объема памяти, имеет модульную структуру и позволяет создавать эффективные программные решения для систем проводной связи с ограниченными аппаратными ресурсами.xosotin chelseathông tin chuyển nhượngcâu lạc bộ bóng đá arsenalbóng đá atalantabundesligacầu thủ haalandUEFAevertonxosofutebol ao vivofutemaxmulticanaisonbetbóng đá world cupbóng đá inter milantin juventusbenzemala ligaclb leicester cityMUman citymessi lionelsalahnapolineymarpsgronaldoserie atottenhamvalenciaAS ROMALeverkusenac milanmbappenapolinewcastleaston villaliverpoolfa cupreal madridpremier leagueAjaxbao bong da247EPLbarcelonabournemouthaff cupasean footballbên lề sân cỏbáo bóng đá mớibóng đá cúp thế giớitin bóng đá ViệtUEFAbáo bóng đá việt namHuyền thoại bóng đágiải ngoại hạng anhSeagametap chi bong da the gioitin bong da lutrận đấu hôm nayviệt nam bóng đátin nong bong daBóng đá nữthể thao 7m24h bóng đábóng đá hôm naythe thao ngoai hang anhtin nhanh bóng đáphòng thay đồ bóng đábóng đá phủikèo nhà cái onbetbóng đá lu 2thông tin phòng thay đồthe thao vuaapp đánh lô đềdudoanxosoxổ số giải đặc biệthôm nay xổ sốkèo đẹp hôm nayketquaxosokq xskqxsmnsoi cầu ba miềnsoi cau thong kesxkt hôm naythế giới xổ sốxổ số 24hxo.soxoso3mienxo so ba mienxoso dac bietxosodientoanxổ số dự đoánvé số chiều xổxoso ket quaxosokienthietxoso kq hôm nayxoso ktxổ số megaxổ số mới nhất hôm nayxoso truc tiepxoso ViệtSX3MIENxs dự đoánxs mien bac hom nayxs miên namxsmientrungxsmn thu 7con số may mắn hôm nayKQXS 3 miền Bắc Trung Nam Nhanhdự đoán xổ số 3 miềndò vé sốdu doan xo so hom nayket qua xo xoket qua xo so.vntrúng thưởng xo sokq xoso trực tiếpket qua xskqxs 247số miền nams0x0 mienbacxosobamien hôm naysố đẹp hôm naysố đẹp trực tuyếnnuôi số đẹpxo so hom quaxoso ketquaxstruc tiep hom nayxổ số kiến thiết trực tiếpxổ số kq hôm nayso xo kq trực tuyenkết quả xổ số miền bắc trực tiếpxo so miền namxổ số miền nam trực tiếptrực tiếp xổ số hôm nayket wa xsKQ XOSOxoso onlinexo so truc tiep hom nayxsttso mien bac trong ngàyKQXS3Msố so mien bacdu doan xo so onlinedu doan cau loxổ số kenokqxs vnKQXOSOKQXS hôm naytrực tiếp kết quả xổ số ba miềncap lo dep nhat hom naysoi cầu chuẩn hôm nayso ket qua xo soXem kết quả xổ số nhanh nhấtSX3MIENXSMB chủ nhậtKQXSMNkết quả mở giải trực tuyếnGiờ vàng chốt số OnlineĐánh Đề Con Gìdò số miền namdò vé số hôm nayso mo so debach thủ lô đẹp nhất hôm naycầu đề hôm naykết quả xổ số kiến thiết toàn quốccau dep 88xsmb rong bach kimket qua xs 2023dự đoán xổ số hàng ngàyBạch thủ đề miền BắcSoi Cầu MB thần tàisoi cau vip 247soi cầu tốtsoi cầu miễn phísoi cau mb vipxsmb hom nayxs vietlottxsmn hôm naycầu lô đẹpthống kê lô kép xổ số miền Bắcquay thử xsmnxổ số thần tàiQuay thử XSMTxổ số chiều nayxo so mien nam hom nayweb đánh lô đề trực tuyến uy tínKQXS hôm nayxsmb ngày hôm nayXSMT chủ nhậtxổ số Power 6/55KQXS A trúng roycao thủ chốt sốbảng xổ số đặc biệtsoi cầu 247 vipsoi cầu wap 666Soi cầu miễn phí 888 VIPSoi Cau Chuan MBđộc thủ desố miền bắcthần tài cho sốKết quả xổ số thần tàiXem trực tiếp xổ sốXIN SỐ THẦN TÀI THỔ ĐỊACầu lô số đẹplô đẹp vip 24hsoi cầu miễn phí 888xổ số kiến thiết chiều nayXSMN thứ 7 hàng tuầnKết quả Xổ số Hồ Chí Minhnhà cái xổ số Việt NamXổ Số Đại PhátXổ số mới nhất Hôm Nayso xo mb hom nayxxmb88quay thu mbXo so Minh ChinhXS Minh Ngọc trực tiếp hôm nayXSMN 88XSTDxs than taixổ số UY TIN NHẤTxs vietlott 88SOI CẦU SIÊU CHUẨNSoiCauVietlô đẹp hôm nay vipket qua so xo hom naykqxsmb 30 ngàydự đoán xổ số 3 miềnSoi cầu 3 càng chuẩn xácbạch thủ lônuoi lo chuanbắt lô chuẩn theo ngàykq xo-solô 3 càngnuôi lô đề siêu vipcầu Lô Xiên XSMBđề về bao nhiêuSoi cầu x3xổ số kiến thiết ngày hôm nayquay thử xsmttruc tiep kết quả sxmntrực tiếp miền bắckết quả xổ số chấm vnbảng xs đặc biệt năm 2023soi cau xsmbxổ số hà nội hôm naysxmtxsmt hôm nayxs truc tiep mbketqua xo so onlinekqxs onlinexo số hôm nayXS3MTin xs hôm nayxsmn thu2XSMN hom nayxổ số miền bắc trực tiếp hôm naySO XOxsmbsxmn hôm nay188betlink188 xo sosoi cầu vip 88lô tô việtsoi lô việtXS247xs ba miềnchốt lô đẹp nhất hôm naychốt số xsmbCHƠI LÔ TÔsoi cau mn hom naychốt lô chuẩndu doan sxmtdự đoán xổ số onlinerồng bạch kim chốt 3 càng miễn phí hôm naythống kê lô gan miền bắcdàn đề lôCầu Kèo Đặc Biệtchốt cầu may mắnkết quả xổ số miền bắc hômSoi cầu vàng 777thẻ bài onlinedu doan mn 888soi cầu miền nam vipsoi cầu mt vipdàn de hôm nay7 cao thủ chốt sốsoi cau mien phi 7777 cao thủ chốt số nức tiếng3 càng miền bắcrồng bạch kim 777dàn de bất bạion newsddxsmn188betw88w88789bettf88sin88suvipsunwintf88five8812betsv88vn88Top 10 nhà cái uy tínsky88iwinlucky88nhacaisin88oxbetm88vn88w88789betiwinf8betrio66rio66lucky88oxbetvn88188bet789betMay-88five88one88sin88bk88xbetoxbetMU88188BETSV88RIO66ONBET88188betM88M88SV88Jun-68Jun-88one88iwinv9betw388OXBETw388w388onbetonbetonbetonbet88onbet88onbet88onbet88onbetonbetonbetonbetqh88mu88Nhà cái uy tínpog79vp777vp777vipbetvipbetuk88uk88typhu88typhu88tk88tk88sm66sm66me88me888live8live8livesm66me88win798livesm66me88win79pog79pog79vp777vp777uk88uk88tk88tk88luck8luck8kingbet86kingbet86k188k188hr99hr99123b8xbetvnvipbetsv66zbettaisunwin-vntyphu88vn138vwinvwinvi68ee881xbetrio66zbetvn138i9betvipfi88clubcf68onbet88ee88typhu88onbetonbetkhuyenmai12bet-moblie12betmoblietaimienphi247vi68clupcf68clupvipbeti9betqh88onb123onbefsoi cầunổ hũbắn cáđá gàđá gàgame bàicasinosoi cầuxóc đĩagame bàigiải mã giấc mơbầu cuaslot gamecasinonổ hủdàn đềBắn cácasinodàn đềnổ hũtài xỉuslot gamecasinobắn cáđá gàgame bàithể thaogame bàisoi cầukqsssoi cầucờ tướngbắn cágame bàixóc đĩaAG百家乐AG百家乐AG真人AG真人爱游戏华体会华体会im体育kok体育开云体育开云体育开云体育乐鱼体育乐鱼体育欧宝体育ob体育亚博体育亚博体育亚博体育亚博体育亚博体育亚博体育开云体育开云体育棋牌棋牌沙巴体育买球平台新葡京娱乐开云体育mu88qh88
Литература
- User Datagram Protocol, RFC 768.
- Internet Protocol, DARPA Internet Program Protocol Specification, RFC 791.
- Internet Control Message Protocol, DARPA Internet Program Protocol Specification, RFC 792.
- Transmission Control Protocol, DARPA Internet Program Protocol Specification, RFC 793.
- Requirements for Internet Hosts, Communication Layers, RFC 1122.
- An Ethernet Address Resolution Protocol or Converting Network Protocol Addresses to 48 bit Ethernet Address for Transmission on Ethernet Hardware, RFC 826.
- Domain Names — Implementation and Specifica-tion, RFC 1035.
- Clarifications to the DNS Specification, RFC 2181.
- Service Name and Transport Protocol Port Number Registry. iana.org