[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT
[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT
0
Components
Hardware componentsSTM32F4-DISCOVERY
x 1
STM32CubeIDE 1.3.1
x 1
Project description
[Environments]
개발 환경은 다음과 같이 사용하였습니다.
- Board : STM32F4-DISCOVERY
- W5100 : Arduino Ethernet Shield
- STM32CubeMx 5.6.1
- STM32CubeIDE 1.3.1
?
[Ethernet Shield Pinout]
아래 schematic 과 같이 SPI 관련 핀들과 전원이 ICSP 커넥터쪽으로 연결되어 있기 때문에 해당 커넥터를 사용해야 합니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTNfMjM1/MDAxNTkyMDA4MjUyMDg0.ebzXk_XHEwYyK6JwVQ4xWkHt4Ud8gl7IlDyD2QYGXAUg.kYHYHFuo7GDRBEaXnCSIKyLdA1ACh0-PUKtVgMAvDrwg.PNG.eziya76/image.png?type=w773)
?
[STM32CubeMx]
기존에는 LwIP 방식으로 TCP 어플리케이션을 구현하였지만 이번에는 외부 TCPIP 칩을 사용하기 때문에 STM32F4-DISCOVERY 보드를 사용하였습니다.
?
1) 전체 Pinout 은 다음과 같습니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMTU5/MDAxNTkxNzQ1NjI2Njcy.g0g37vmYsA67jMWjsA6GZpw6cuT0p599xSCfWySFG_sg.Cp23NM5xKflhZ1SSJyzDPA9fh9FHYEKYI2EQ1zjGV0Ug.PNG.eziya76/image.png?type=w773)
2) RCC 항목에서 Clock 소스를 설정하여 줍니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMjc2/MDAxNTkxNzQ1Njc4ODQ0.cYqRBp5ClKeZ16VBjJhZP30yQ0VeRlpyKW3Q2PQz0Ngg.qYz3xAmh2KCeJvF0IosP2lAxol-Rl9WB0EInRm8HyPEg.PNG.eziya76/image.png?type=w773)
3) SYS 항목에서 Debug 와 Timebase 소스를 설정하여 줍니다. FreeRTOS 를 사용하기 때문에 Timebase Source 를 TIM6 로 변경하여 줍니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMTAz/MDAxNTkxNzQ1NzM3NTc1.g6WdB5rUzGMIodKmMtgWqAZf1CVfIIsQxMuyleDcVAEg.55KT2s9rUSZaCBKJam-H0JBCzhVHgpr4bFI5tcm_23Qg.PNG.eziya76/image.png?type=w773)
4) SPI1 항목을 Full-Duplex Master 로 설정하여 줍니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMjky/MDAxNTkxNzQ2MTc1NDY1.RB7ntrwq_QxDirClUjtzcf2yX1jdrGVL1AdjaaN1tKcg.G7aY0_2ipwl9o4HlnGSVE664Mwv_Q4NDXd-miZpudOgg.PNG.eziya76/image.png?type=w773)
W5100 은 데이터시트에 70ns 로 적혀있는데 대략 13Mbps 정도 되니,
Prescaler 값은 8로 설정하여 Baudrate 는 10.5Mbps 로 설정합니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMjY4/MDAxNTkxNzQ2MTk3MzEz.6MngYN9IcTQk3QJUUEfE1pwowNY1Cirsz1XkIT_-sSIg.Fsg36qPWgT-6dvxywdR9X4-cn9yg9IhqGVYstJMdd_og.PNG.eziya76/image.png?type=w773)
5) FREERTOS 항목을 아래와 같이 설정하여 줍니다.
Default 설정값에서 FPU, stack 사이즈, heap 사이즈, stack overflow check 등을 수정하였습니다.
Task 는 Default Task 만 정의하고 Tcp Client Task 는 Default Task 에서 직접 생성하는 방식으로 작업합니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfNjIg/MDAxNTkxNzQ2MzMwOTky.qaQz-se6Doaq2voFx3rXLflQAnqMo0vcCWymVhra98Eg.ws41BWeQeUV9_uipxdR5CzaZp056VDZj7hyxfAK5Mgcg.PNG.eziya76/image.png?type=w773)
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMTA1/MDAxNTkxNzQ2MzYwMzk3.VZT5RUEEylkUa2t7NLS3pOt8VSjFs6OstzouGWoJnj8g.jPUJGAv0Me0o8iAlQp_5l9ZGMiK2UKE5RsDHONogjbcg.PNG.eziya76/image.png?type=w773)
6) Clock Configuration 으로 이동하여 Clock 은 168MHz max 클럭으로 동작하도록 설정합니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMjE4/MDAxNTkxNzQ2NTE2MzAw.ayc-qX4Ows6oCYJNNo3SxmxeSYX5wcSQoo99MfacTegg.Ls0d9PB_fzhSRqPAV41f3jfpz22TwMiJ58SUkN29tlAg.PNG.eziya76/image.png?type=w773)
7) Project Manager 탭에서 프로젝트 명칭 및 code generator 옵션를 설정하여 준 후 프로젝트를 생성합니다.
?
[Project 구성]
프로젝트를 변경하는 지점은 크게 4가지 입니다.
1) ioLibrary 추가
References 를 참고하여 WIZNET ioLibrary 를 다운로드 받은 후 /Middlewares/Third_Party 폴더에 아래와 같이 추가하여 줍니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfOTEg/MDAxNTkxNzQ2ODkxNTIz.H7kBMG6VOfRfedPSj23wT3plvOifuwRuP_FusJylKIcg.C7LtaAmDNXMVtf8-lE6wjRVNtTW3sIAGv2Q3kAihdJog.PNG.eziya76/image.png?type=w773)
2) include path 설정
GCC 설정에서 아래와 같이 Ethernet, W5100, DHCP 폴더의 path 를 include paths 에 추가하여 줍니다.
![[STM32 HAL] WIZNET W5100(W5500) + TCP CLIENT](https://postfiles.pstatic.net/MjAyMDA2MTBfMTMw/MDAxNTkxNzQ2OTg3Nzk1.XSojmxG8MYmpVOTdNuNN25OksoOIJcDIdqaIfVDt8rMg.L000l8J8UT0P15sT0L2vxMl0rS8s0LfYhaPQR_mdqtgg.PNG.eziya76/image.png?type=w773)
?
3) wizInterface.c/h 파일 추가
wizinterface.c/h 파일은 W5100 의 SPI 통신, 초기화, 설정과 관련된 함수들을 위한 파일입니다.
세부 내용은 아래 Sources 항목에서 설명하겠습니다.
?
4) tcpclient.c/h 파일 추가
tcpclient.c/h 파일은 FreeRTOS 에서 동작하는 Tcp Client Task 를 위한 파일입니다.
세부 내용은 아래 Sources 항목에서 설명하겠습니다.
?
[Sources]
코드 작업이 필요한 파일은, main.c | freertos.c | wizinterface.c/h | tcpclient.c/h 입니다.
?
1) main.c
main.c 에서 필요한 작업은 아래와 같습니다.
?
- SWV ITM 출력을 위한 _write() 함수 재정의
- FreeRTOS 에서 printf exception 발생을 막기위한 코드 추가
- ioLibrary DHCP 동작을 위한 DHCP_time_handler() 관련 코드 추가
2) freertos.c
freertos.c 파일에서 작업이 필요한 내용은 다음과 같습니다.
?
- vApplicationStackOverflowHook 함수에 stack overflow 발생 시, LED 제어
- StartDefaultTask 함수에서 wizTcpClientTask 수행
3) wizinterface.c/h
wizinterface.c 파일에서는 다음과 같은 작업을 수행합니다.
- W5100 과 SPI 통신을 위한 함수 정의 (WIZ_SPI_XXX 함수들)
- DHCP 관련 callback 함수 정의 (cbIPAddrAssigned, cbIPAddrConflic)
- W5100 초기화 함수 정의 (WIZ_ChipInit)
- W5100 네트워크 설정 함수 정의 (WIZ_NetworkInit)
4) tcpclient.c/h
tcpclient.c 파일의 StartWizTcpClientTask 에서는 다음과 같은 작업을 수행합니다.
- WIZ_ChipInit 호출하여 초기화
- WIZ_NetworkInit 호출하여 네트워크 설정 (IP 설정 또는 DHCP 를 통한 IP 할당)
- socket API 를 호출하여 소켓 생성
- connect API 를 호출하여 서버 접속
- send API 를 호출하여 REQ 전송
- recv API 를 호출하여 RESP 수신
- 수신된 데이터를 체크
- close API 를 호출하여 소켓 close
- 이후 동작은 10ms delay 를 갖고 소켓 생성부터 반복
socket() 함수와 connect() 함수 사이의 delay 가 현상을 줄여주기는 합니다만 완벽하게 없어지지는 않습니다. //create socket ret = socket(CLIENT_SOCKET, Sn_MR_TCP, localPort++, 0); if(localPort == 0xFFFF) { localPort = 50000; } if (ret < 0) { HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin); printf("socket failed{%ld}.n", ret); ...
forum.wiznet.io
"설명에 앞서 제가 수정한 방법은 제가 겪은 오류만을 회피할 뿐 다른 예상치 못한 악영향을 미칠 수도 있습니다. "
?
테스트를 하면서 앞서 언급한 것과 같이 매우 빠른 주기로 socket-connect-send-recv-close 를 반복하면 빈번하게 connect 시에 -4(SOCKERR_SOCKCLOSED) 를 반환하면서 접속에 실패하고 Sn_SR 값이 0x10 으로 정의되지 않은 값을 반환하는 문제가 있었습니다.
?
우선 문제가 되는 부분은 접속이 실패하는 것은 괜찮은데 이후 에러 처리 시 소켓을 정리하기 위한 close 함수 호출 시 무한 loop 에 걸리면서 task 가 중지하는 것이 문제 였습니다.
?
1) close()
아래 코드를 보면,
while(get_Sn_SR(sn) != SOCK_CLOSED);
는 SOCK_CLOSED 가 되지 않으면 무한 대기를 하기 되어 있습니다. 그런데 제가 확인한 현상은
?
close() 함수에서 setSn_CR(sn,Sn_CR_CLOSE) 을 이용해서 CLOSE 명령을 전달한 후에 Sn_SR 상태가 SOCK_ESTABLISHED 로 변경되면서 무한 루프에 빠지는 현상이었습니다.
?
이런 케이스가 발생하는 것을 우회하기 위해서 Sn_SR 상태가 SOCK_CLOSED 가 아니라면 무한 대기 하는 것이 아니라 다시 CLOSE 명령을 전송하도록 변경하였습니다.
2) connect()
앞서 close 시에 Sn_SR 의 상태가 SOCK_ESTABLISHED 가 된다고 말씀을 드렸는데 그 원인이 저는 connect 에 있다고 보여집니다.
?
실제 connect 함수가 호출 후에 SOCK_ESTABLISHED 로 상태가 변경되는데 필요한 시간이 길어질 때까 있는데 이 경우에 대한 예외 코드가 되어 있지 않아서 그대로 Error 를 반환해 버리는데 이때 connect 가 에러를 주었기 때문에 socket 을 close 하려고 하면 앞선 close 의 문제가 발생하는 것입니다.
?
이 문제를 우회하기 위해서는 connect 시에 상태가 SOCK_ESTABLISHED 가 되지 않았더라도 바로 에러를 반환하는 것이 아니라 몇차례 retry 를 하도록 수정하였습니다.
[ 테스트 ]
테스트는 github 에 올려놓은 TcpServer 테스트 어플리케이션을 이용하여 진행하였습니다.
https://github.com/eziya/STM32F4_HAL_LWIP_LAB/tree/master/MyTcpServer
STM32 HAL LwIP Testing. Contribute to eziya/STM32F4_HAL_LWIP_LAB development by creating an account on GitHub.
github.com
