Продолжаем работать со стеком протоколов LWIP с интерфейсом SOCKET и теперь мы попробуем подключить к нашей плате STM32F746-Discovery ещё одну такую же плату по интерфейсу LAN. Подобными действиями мы уже занимались ранее, но использовали при этом мы другие интерфейсы. А сегодня мы организуем на одной плате клиент, а на другой сервер, используя API SOCKET.
Начнём, как всегда, с сервера.
Проект за основу возьмём из прошлого урока с именем LAN8742_UDP_SERVER_SOCKET и присвоим ему имя LAN8742_UDP_SERVER_SOCKET1, чтобы имена хоть немного отличались.
Откроем проект в Cube MX и, ничего в нём не трогая, сгенерируем проект для System Workbench, откроем его там, уберём отладочные настройки при их наличии, а также изменим уровень оптимизации на 1.
Передавать клиенту мы будем, как обычно, количество системных квантов, прошедших с момента включения или перезагрузки микроконтроллера.
Откроем файл main.c и в функции udp_thread добавим переменную
1 2 |
struct_sock *arg_sock; uint32_t syscnt = 0; |
После приёма пакета заберём величину, присланную в пакете клиентом в переменную
1 2 |
qstruct->sfont = Font24; syscnt = *(uint32_t*) recv_buffer; |
Удалим вот эту строку
recv_buffer[ret-1] = 0;
В следующей строке немного изменим аргументы
sprintf(qstruct->str,"%5u %10lu", ntohs(remotehost.sin_port), syscnt);
Преобразуем милисекунды в секунды и передадим значение серверу
1 2 3 |
osMailPut(strout_Queue, qstruct); syscnt /= 1000; sendto(sock,(void *) &syscnt,4,0,(struct sockaddr *)&remotehost, sockaddrsize); |
Старую передачу строки удалим
strcat((char*)recv_buffer,«\n»);
sendto(sock,recv_buffer,strlen((char*)recv_buffer),0,(struct sockaddr *)&remotehost, sockaddrsize);
Соединим две платы кабелем RJ45, ту, которая будет сервером, подключим по USB к ПК, к другой пока питание не подключаем.
Соберём код, прошьём контроллер первой платы.
Затем к первой платы подведём питание от независимого источника, а кабель USB от ПК подключим ко второй плате и займёмся теперь клиентом.
В качестве проекта для клиента возьмём за основу проект для сервера и назовём его, соответственно, LAN8742_UDP_CLIENT_SOCKET1.
Откроем проект в Cube MX и исправим в настройках LWIP адрес IP
Также в настройках ETH изменим MAC-адрес
Сгенерируем проект для System Workbench, откроем его там, уберём отладочные настройки при их наличии, а также изменим уровень оптимизации на 1.
Откроем файл main.c и для начала изменим шапку в функции main()
TFT_DisplayString(0, 10, (uint8_t *)"UDP Client", CENTER_MODE);
Передавать данные серверу мы будем в отдельной задаче, поэтому выше функции udp_thread добавим следующую функцию
1 2 3 4 5 6 7 8 9 |
//--------------------------------------------------------------- static void send_thread(void *arg) { for(;;) { osDelay(1000); } } //--------------------------------------------------------------- |
Создадим также глобальную структуру для передачи параметров в данную задачу, а также глобальную переменную типа данной структуры
1 2 3 4 5 |
struct_conn conn01; typedef struct struct_sock_send_t { int sock; } struct_sock_send; struct_sock_send sock_send01; |
Структура состоит только из одного поля. Можно было по идее передать параметр через обычную переменную, но со структурой удобнее.
В функции StartDefaultTask изменим номер порта для клиента
sock01.port = 1551;
В функции udp_thread после связи сокета с интерфейсом произведём инициализацию поля структуры и создадим задачу для передачи данных серверу
1 2 3 4 |
if (bind(sock, (struct sockaddr *)&address, sizeof (address)) == 0) { sock_send01.sock = sock; sys_thread_new("send_thread", send_thread, (void*)&sock_send01, DEFAULT_THREAD_STACKSIZE, osPriorityNormal ); |
Так как передача пакетов у нас будет происходить в отдельной задаче, то удалим передачу из задачи udp_thread, заодно удалим и отображение минимального остатка кучи
osMailPut(strout_Queue, qstruct);
syscnt /= 1000;
sendto(sock,(void *) &syscnt,4,0,(struct sockaddr *)&remotehost, sockaddrsize);
osDelay(2);
sprintf(qstruct->str, «%7u»,xPortGetMinimumEverFreeHeapSize());
qstruct->y_pos = arg_sock->y_pos + 40;
Теперь перейдём в задачу send_thread и добавим переменные типа структур для очереди и для параметров
1 2 3 4 |
static void send_thread(void *arg) { struct_sock_send *arg_sock_send; struct_out *qstruct; |
Присвоим адресу структуры адрес параметра
1 2 |
struct_out *qstruct; arg_sock_send = (struct_sock_send*) arg; |
Объявим переменную для количества системных квантов
1 2 |
arg_sock_send = (struct_sock_send*) arg; uint32_t syscnt = 0; |
Добавим пару структур для сокета
1 2 3 |
uint32_t syscnt = 0; struct sockaddr_in remotehost; socklen_t sockaddrsize; |
Вычислим размер переменной структуры интерфейса сервера и произведём её инициализацию, внеся данные сервера (номер порта, адрес IP)
1 2 3 4 5 |
socklen_t sockaddrsize; sockaddrsize = sizeof(remotehost); remotehost.sin_family = AF_INET; remotehost.sin_port = htons(7); ip4addr_aton("192.168.1.191",(ip4_addr_t*)&remotehost.sin_addr); |
В бесконечном цикле определим количество системных квантов, прошедших с момента включения или перезагрузки устройства и передадим его серверу
1 2 3 4 |
for(;;) { syscnt = osKernelSysTick(); sendto(arg_sock_send->sock,(void *) &syscnt,4,0,(struct sockaddr *)&remotehost, sockaddrsize); |
Отобразим также количество системных квантов и номер порта клиента на дисплее
1 2 3 4 5 6 7 8 |
sendto(arg_sock_send->sock,(void *) &syscnt,4,0,(struct sockaddr *)&remotehost, sockaddrsize); qstruct = osMailAlloc(strout_Queue, osWaitForever); qstruct->x_pos = 0; qstruct->y_pos = 120; qstruct->sfont = Font24; sprintf(qstruct->str,"%5u %10lu",1551, syscnt); osMailPut(strout_Queue, qstruct); osMailFree(strout_Queue, qstruct); |
Ну вот и всё с клиентом.
Соберём код, прошьём контроллер, возможно придётся перезагрузить и сервер.
Посмотрим результат передачи данных между двумя устройствами
Всё работает отлично! Обмен происходит правильно.
Таким образом, в данном уроке мы научились создавать клиент UDP, используя API SOCKET стека протоколов LWIP, что позволило нам соединить две платы по LAN и организовать между ними обмен данными.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь 32F746G-DISCOVERY
Смотреть ВИДЕОУРОК (нажмите на картинку)
Ссылка на видео у меня выглядит как ********
Поправил, спасибо!