Ten przykład chciałbym poświęcić na konfigurację STM32, w wersji bez RTOS, jako serwer ethernet.
Tutaj chciałbym opisać dokładniej sposoby inicjalizacji biblioteki LWIP w wersji RAW, czyli bez systemu. Program przygotowałem w oparciu o pliki udostępnione przez ST. Które opierają się na przykładzie przygotowanym przez autora LWIP.
Na samym początku należy uruchomić bibliotekę wygenerowaną przez Cube lub skopiowaną w odpowiedniej wersji. Wykonuje się to poleceniem:
MX_LWIP_Init();
W pętli głównej lub w przerwaniach od timera należy wywoływać funkcje resetującą wszystkie zmienne zawierające informacje o czasie trwania operacji.
sys_check_timeouts();
Proces stawiania połączenia należy rozpocząć od wywołania funkcji tcp_new aby stworzyło nowy blok z danymi dla TCP.
Aby stworzyć nowe połączenie należy wywołać funkcje:
Domyślnie będzie ona zawierała wartość NULL, i taką samą utrzyma jeśli proces tworzenia się nie powiedzie.
Następnie należy wywołać funkcje przypisującą adress IP oraz Port:
Jeśli port zostanie podany jako 0 to przypisanie nastąpi do wolnego portu. Aby ją wywołać to połączenie musi być ciągle zamknięte. Ja wykorzystuje zwykły port, można równie dobrze wykorzystać port 7 który jest przypisany jako echo. Funkcja zwróci wartość ERR_OK jeśli proces przejdzie prawidłowo lub ERR_USE, gdy port jest już wykorzystywany.
Poniżej funkcja inicjalizująca echoserver.
Po wywołaniu funkcji tcp_bind należy wywołać funkcje tcp_listen, która czeka na akceptacje połączenia. Zwraca ona nowy blok kontrolny dla zainicjalizowanego połączenia. Porzuca ona wcześniejszy i uruchamia nowy, który potrzebuje mniejszej ilości pamięci. Po jej wywołaniu należy wywołać tcp_accept. W przeciwnym wypadku stworzenie połączenia zostanie przerwane. Jeśli połączenia nie uda się nawiązać, to następuje wyczyszczenie przydzielonej pamięci.
Funkcja odbierająca dane:
Obsługa błędu odbywa się przy użyciu następującej funkcji:
Funkcja odpowiedzialna za oczekiwanie na przesłanie danych:
Sprawdzanie dostępności danych:
Przesłanie danych:
Czyszczenie zmiennych, zamknięcie połączenia:
W głównej części programu należy wywołać tylko:
Funkcje inicjalizującą bibliotekę LWIP należy wywołać tylko jeden raz, ponowne wywołania zawiesi układ.
Pliki do projektu można pobrać pod tym linkiem. Po wygenerowaniu projektu przez Cube Mx należy podmienić biblioteki i dodać pliki których brakuje.
[Źródło: http://www.st.com/en/evaluation-tools/32f746gdiscovery.html]
Programowanie[link]:
Tutaj chciałbym opisać dokładniej sposoby inicjalizacji biblioteki LWIP w wersji RAW, czyli bez systemu. Program przygotowałem w oparciu o pliki udostępnione przez ST. Które opierają się na przykładzie przygotowanym przez autora LWIP.
Na samym początku należy uruchomić bibliotekę wygenerowaną przez Cube lub skopiowaną w odpowiedniej wersji. Wykonuje się to poleceniem:
MX_LWIP_Init();
W pętli głównej lub w przerwaniach od timera należy wywoływać funkcje resetującą wszystkie zmienne zawierające informacje o czasie trwania operacji.
sys_check_timeouts();
Proces stawiania połączenia należy rozpocząć od wywołania funkcji tcp_new aby stworzyło nowy blok z danymi dla TCP.
Aby stworzyć nowe połączenie należy wywołać funkcje:
- struct tcp_pcb * tcp_new(void)
- tcp new();
Domyślnie będzie ona zawierała wartość NULL, i taką samą utrzyma jeśli proces tworzenia się nie powiedzie.
Następnie należy wywołać funkcje przypisującą adress IP oraz Port:
- err_t tcp_bind(struct tcp_pcb * pcb, struct ip_addr * ipaddr, u16_t port)
- tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 1001);
Jeśli port zostanie podany jako 0 to przypisanie nastąpi do wolnego portu. Aby ją wywołać to połączenie musi być ciągle zamknięte. Ja wykorzystuje zwykły port, można równie dobrze wykorzystać port 7 który jest przypisany jako echo. Funkcja zwróci wartość ERR_OK jeśli proces przejdzie prawidłowo lub ERR_USE, gdy port jest już wykorzystywany.
Poniżej funkcja inicjalizująca echoserver.
- /*
- * @brief: Init echo server
- * @param: none
- * @ret: none
- */
- void tcp_echoserver_init(void)
- {
- /* Create new connection control block */
- tcp_echoserver_pcb = tcp_new();
- if (tcp_echoserver_pcb != NULL)
- {
- if (tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 1001) == ERR_OK)
- {
- tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
- tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);
- }
- else /* tcp_bind return with ERR_USE, port is already used */
- {
- /* Set selected element free, clear all settings for it */
- memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb);
- }
- }
- }
Po wywołaniu funkcji tcp_bind należy wywołać funkcje tcp_listen, która czeka na akceptacje połączenia. Zwraca ona nowy blok kontrolny dla zainicjalizowanego połączenia. Porzuca ona wcześniejszy i uruchamia nowy, który potrzebuje mniejszej ilości pamięci. Po jej wywołaniu należy wywołać tcp_accept. W przeciwnym wypadku stworzenie połączenia zostanie przerwane. Jeśli połączenia nie uda się nawiązać, to następuje wyczyszczenie przydzielonej pamięci.
- static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
- {
- err_t ret_err;
- struct tcp_echoserver_struct *es;
- /* Unused arguments to prevent warnings */
- (void)arg;
- (void)err;
- /* Set priority for new connection */
- tcp_setprio(newpcb, TCP_PRIO_MIN);
- /* allocate structure with info about tcp connection */
- es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));
- if (es != NULL)
- {
- es->state = ES_ACCEPTED;
- es->pcb = newpcb;
- es->retries = 0;
- es->p = NULL;
- /* Pass structure data do new connection */
- tcp_arg(newpcb, es);
- /* prepare to receive data */
- tcp_recv(newpcb, tcp_echoserver_recv);
- /* if error ocure then tha t callback will be */
- tcp_err(newpcb, tcp_echoserver_error);
- /* waits for connection */
- tcp_poll(newpcb, tcp_echoserver_poll, 0);
- ret_err = ERR_OK;
- }
- else
- {
- /* Close connection */
- tcp_echoserver_connection_close(newpcb, es);
- /* return error */
- ret_err = ERR_MEM;
- }
- return ret_err;
- }
Funkcja odbierająca dane:
- static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
- {
- struct tcp_echoserver_struct *es;
- err_t ret_err;
- LWIP_ASSERT("arg != NULL",arg != NULL);
- es = (struct tcp_echoserver_struct *)arg;
- /* If buffer is empty */
- if (p == NULL)
- {
- /* Close connection */
- es->state = ES_CLOSING;
- if(es->p == NULL)
- {
- /* Close connection */
- tcp_echoserver_connection_close(tpcb, es);
- }
- else
- {
- /* Callback function used when data was received */
- tcp_sent(tpcb, tcp_echoserver_sent);
- /* Send data back to server */
- tcp_echoserver_send(tpcb, es);
- }
- ret_err = ERR_OK;
- }
- else if(err != ERR_OK)
- {
- /* Error occure, clear buffer */
- if (p != NULL)
- {
- es->p = NULL;
- pbuf_free(p);
- }
- ret_err = err;
- }
- else if(es->state == ES_ACCEPTED)
- {
- /* connection accept, first data received, chunk in p->payload */
- es->state = ES_RECEIVED;
- /* write data to structuce*/
- es->p = p;
- tcp_sent(tpcb, tcp_echoserver_sent);
- /* Send data */
- tcp_echoserver_send(tpcb, es);
- ret_err = ERR_OK;
- }
- else if (es->state == ES_RECEIVED)
- {
- /* All data received */
- if(es->p == NULL)
- {
- es->p = p;
- tcp_echoserver_send(tpcb, es);
- }
- else
- {
- struct pbuf *ptr;
- /* chain two to the end of what we recv'ed previously */
- ptr = es->p;
- pbuf_chain(ptr,p);
- }
- ret_err = ERR_OK;
- }
- else if(es->state == ES_CLOSING)
- {
- //odd case, remote side closing twice, free all trash data
- tcp_recved(tpcb, p->tot_len);
- es->p = NULL;
- pbuf_free(p);
- ret_err = ERR_OK;
- }
- else
- {
- //unkown es->state, trash data
- tcp_recved(tpcb, p->tot_len);
- es->p = NULL;
- pbuf_free(p);
- ret_err = ERR_OK;
- }
- return ret_err;
- }
Obsługa błędu odbywa się przy użyciu następującej funkcji:
- static void tcp_echoserver_error(void *arg, err_t err)
- {
- struct tcp_echoserver_struct *es;
- (void)err;
- es = (struct tcp_echoserver_struct *)arg;
- if (es != NULL)
- {
- /* free structure */
- mem_free(es);
- }
- }
Funkcja odpowiedzialna za oczekiwanie na przesłanie danych:
- static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb)
- {
- err_t ret_err;
- struct tcp_echoserver_struct *es;
- es = (struct tcp_echoserver_struct *)arg;
- if (es != NULL)
- {
- if (es->p != NULL)
- {
- tcp_sent(tpcb, tcp_echoserver_sent);
- /* There is data in pbuf, chain try to send it */
- tcp_echoserver_send(tpcb, es);
- }
- else
- {
- /* No data in chain */
- if(es->state == ES_CLOSING)
- {
- /* Close connection */
- tcp_echoserver_connection_close(tpcb, es);
- }
- }
- ret_err = ERR_OK;
- }
- else
- {
- /* Aborts the connection by sending a RST (reset) segment to the remote host */
- tcp_abort(tpcb);
- ret_err = ERR_ABRT;
- }
- return ret_err;
- }
Sprawdzanie dostępności danych:
- static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
- {
- struct tcp_echoserver_struct *es;
- (void)len;
- es = (struct tcp_echoserver_struct *)arg;
- es->retries = 0;
- if(es->p != NULL)
- {
- /* data still in buffer */
- tcp_sent(tpcb, tcp_echoserver_sent);
- tcp_echoserver_send(tpcb, es);
- }
- else
- {
- /* No data to send close connection */
- if(es->state == ES_CLOSING)
- {
- tcp_echoserver_connection_close(tpcb, es);
- }
- }
- return ERR_OK;
- }
Przesłanie danych:
- #define USART_COPY 1
- static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
- {
- struct pbuf *ptr;
- #if USART_COPY == 1
- char dane[256] = {0};
- char buffer[256] = {0};
- #endif
- err_t wr_err = ERR_OK;
- /* tcp_sndbuf - returns number of bytes in space that is avaliable in output queue */
- while ((wr_err == ERR_OK) && (es->p != NULL) && (es->p->len <= tcp_sndbuf(tpcb)))
- {
- /* get pointer on pbuf from es structure */
- ptr = es->p;
- #if USART_COPY == 1
- sprintf(dane, "%s",(char *)ptr->payload);
- for(uint8_t i = 0; i<ptr->len; i++)
- {
- buffer[i] = dane[i];
- }
- /* Close connection */
- if(buffer[0] == 'E' && buffer[1] == 'N' && buffer[2] == 'D') {
- tcp_echoserver_connection_close(tpcb, es);
- }
- else{
- Usart_Uart_SendString(USART1, buffer, LF_CR);
- }
- #endif
- //wr_err = tcp_write(tpcb, buffereth, strlen(buffereth), 1);
- wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
- /* Clear data */
- memset(dane, 0x00, sizeof(dane));
- if (wr_err == ERR_OK)
- {
- u16_t plen;
- u8_t freed;
- plen = ptr->len;
- /* continue with next pbuf in chain (if any) */
- es->p = ptr->next;
- if(es->p != NULL)
- {
- /* increment reference count for es->p */
- pbuf_ref(es->p);
- }
- /* chop first pbuf from chain */
- do
- {
- /* try hard to free pbuf */
- freed = pbuf_free(ptr);
- }
- while(freed == 0);
- /* we can read more data now */
- tcp_recved(tpcb, plen);
- }
- else if(wr_err == ERR_MEM)
- {
- /* we are low on memory, try later / harder, defer to poll */
- es->p = ptr;
- }
- else { }
- }
- }
Czyszczenie zmiennych, zamknięcie połączenia:
- static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
- {
- //remove all callbacks
- tcp_arg(tpcb, NULL);
- tcp_sent(tpcb, NULL);
- tcp_recv(tpcb, NULL);
- tcp_err(tpcb, NULL);
- tcp_poll(tpcb, NULL, 0);
- /* free structure */
- if (es != NULL)
- {
- mem_free(es);
- }
- /* Connection close */
- tcp_close(tpcb);
- }
W głównej części programu należy wywołać tylko:
- //...
- //...
- //...
- MX_LWIP_Init();
- tcp_echoserver_init();
- while(1)
- {
- ethernetif_input(&gnetif);
- sys_check_timeouts();
- }
Funkcje inicjalizującą bibliotekę LWIP należy wywołać tylko jeden raz, ponowne wywołania zawiesi układ.
Pliki do projektu można pobrać pod tym linkiem. Po wygenerowaniu projektu przez Cube Mx należy podmienić biblioteki i dodać pliki których brakuje.