RTOS TCP/IP

kenGwon·2024년 1월 26일

[STM32] Firmware/RTOS

목록 보기
10/11

셋팅

STM32 F429zi의 데모보드가 만들어져서 납품될 때 회사 ID까지는 다 적혀서 오는데, 시리얼 넘버는 적혀서 오지 않았다. 그러므로 맥주소를 먼저 설정해줘야 한다.
이 작업을 하지 않으면 TCP/IP OS7레이어에서 Physical Layer에서 Network Interface에서 패킷이 가야할 주소를 제대로 찾지 못해서 모튼 패킷을 체크하게되어 통신이 되지 않는다.

MAC Address 설정

  1. ioc파일을 열어서 "Conectivity"탭에서 "ETH" 탭으로 들어간다.

  2. 거길 보면 mode가 RMII로 설정되어 있는 것을 볼 수 있다.

  3. "parameter setting"탭을 보면 Ethernet MAC Address가 있는데, 앞에 3바이트는 회사 ID라서 이미 적혀있는 것을 알 수 있다.(00:80:E1 = STM32 회사ID)

  4. 뒤에있는 3바이트가 내가 가지고 있는 데모보드의 하드웨어 시리얼 넘버가 되는 것인데, 그게 모든 학생이 전부 00:00:00으로 되어있는 것을 알 수 있다.

  5. 내 보드는 지금 "STM32 IP address: 10.10.15.82/24"라는 IP를 쓰도록 되어있는데, 여기서 맨 마지막 "82"를 맥 어드레스에도 적용하는 것으로 우리만의 룰을 설정하도록 하겠다.(그냥 권선일 교수님이 룰을 정한 것.)

  6. 최종적으로 내 보드의 MAC Address는 "00:80:E1:00:00:82"가 되는 것이다.

  7. 이 값은 RAM에 저장되는게 아니라, 비휘발성 메모리에 저장되게 되어 전원을 껏다 켜도 유지되게 된다.

  8. 이렇게 해줌으로써 우리 반 18명 보드의 MAC Address가 충돌하지 않게되어 제대로된 통신이 가능하게 된다.

  9. 그리고 마지막으로 잊지 말고, NVIC setting에서 인터럽트를 모두 enable시켜줘야 한다!!! 그래야 패킷 인터럽트가 떠서 제대로 통신이 되게 된다.

여기까지 해서 Layer1, 2에 대한 작업이 끝났다.

이제 3, 4 레이어에 대한 작업을 해줘야 한다.

  1. "Middleware and Sofrware package"탭으로 간다.
  2. LWIP 탭으로 이동하여 LWIP를 enable시켜준다.
  3. 그리고 나서 General Settings를 보면 IPv4에 대한 DHCP 옵션이 enable로 되어있는데, 우리는 이것은 disable로 바꾸고 고정IP를 할당하여 사용할 것이다.
  4. 거기에 적을 내용은 아래와 같다.(내 PC IP address: 10.10.15.70/24)
    • STM32 IP address: 10.10.15.82/24
    • NETMASK: 255.255.255.000
    • Gateway: 10.10.15.254
  5. 그리고 "platform setting" 탭으로 이동하여 설정을 해줘야 한다.
  6. 우리는 LAN8742로 설정을 해줬는데, 그걸 그냥 해주는게 아니다. 우리 GUI 핀맵을 보면 PA7, PC4, PC5 포트에 RMII_CRS_DV [LAN8742A-CZ-TR_CRS_DV] 이런 값이 써져있는 것을 볼 수 있다. 이것을 보고서 우리 보드의 피지컬 플랫폼이 LAN8742라고 판단하여 이렇게 셋팅한 것이다.

여기까지 셋팅을 마치고 code generation을 해서 우리가 설정한 대로 코드가 만들어졌는지 확인해 볼 수 있다.

자동 생성된 LWIP 폴더를 살펴보자.

그 폴더 안에 APP 폴더가 있고, TARGET 폴더가 있다.

  • APP 폴더 안에 있는 lwip.c에는 ip 관련 정보가 들어있는 것이고,
  • TARGET 폴더 안에 있는 etehrnetif.c에는 MAC 관련 정보가 들어가 있다.

STM32CubeF4 Firmware Package

이제 STM32에서 배포한 패키지를 골격으로 해서 좀더 쉽게 어플리케이션을 만들어보겠다.

STM32이 배포하는 패키지를 여기서 다운로드 받으면 되는데, 우리의 경우 교수님이 미리 다운받아서 배포해주셨다.

STM32Cube MCU Package for STM32F4 series (HAL, Low-Layer APIs and CMSIS, USB, TCP/IP, File system, RTOS, Graphic - and examples running on ST boards)

툴킷의 풀네임을 보면 대충 무슨 패키지인지 알 수 있겠다.

압축 해제 후 폴더를 살펴보면서 우리가 필요한 샘플코드를 찾자.

핵심경로

. >> Projects >> STM324xG_EVAL >> Applications >> LwIP >> LwIP_UDP_Echo_Server >> Inc, Src

위 경로에서 "udp_echoserver.h"와 "udp_echoserver.c"를 복사해다가, 현재 우리가 작업중인 프로젝트 경로에다가 넣어주면 된다. (이 붙여넣기 작업을 윈도우 파일탐색기를 통해 하면 안되고, cubeIDE 상에서 붙여넣기 작업을 해야 제대로 IDE에 파일이 잡힌다.)

셋팅 이어서

main.c

  1. main.c에다가 우선 외부에서 가져온 udp_echoserver.h을 include했다.
  2. 그리고 우리 프로그램이 현재 RTOS를 통해서 제어되고 있기 때문에, 소켓을 초기화 하고 패킷을 수신하는 작업을 우선 defaulttask 쪽에 넣어놨다. 상세한 함수 내용은 다음과 같다.
/*
 * global variables for TCP/IP
 */
ip_addr_t addr1; // client addr
struct pbuf *p1; // send buffer
char temp_str[40]; // temp string buffer
struct udp_pcb *upcb1; // udp control block
extern uint8_t udp_data[100]; // udp data

void StartDefaultTask(void *argument)
{
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN 5 */

  /*
   * TCP/IP UDP initialize START
   */
  udp_echoserver_init();
  upcb1 = udp_new(); // 메모리 동적할당
  IP4_ADDR(&addr1, 10, 10 , 15, 70); // set client address(kenGwon's local DESKTOP) to STM32F429ZI via ip_addr_t
  udp_bind(upcb1, IP_ADDR_ANY, 9999); // bind IP address and port number to UDP socket
  /*
   * TCP/IP UDP initialize END
   */


  /* Infinite loop */
  for(;;)
  {
	ethernetif_input(&gnetif);
	sys_check_timeouts();

	/*
	 * TCP/IP UDP communication START
	 */
	udp_connect(upcb1, &addr1, 9999);
	sprintf(temp_str, "Hello, kenGwon");
	p1 = pbuf_alloc(PBUF_TRANSPORT, strlen((char*)temp_str), PBUF_POOL);

	if (p1 != NULL) // success dynamic memory allocation
	{
		// 1. copy data to PBuffer
		pbuf_take(p1, (char*)temp_str, strlen((char*)temp_str));
		// 2. send UDP data
		udp_send(upcb1, p1);
		// 3. free the UDP connection
		udp_disconnect(upcb1);
		// 4. free PBuffer
		pbuf_free(p1);
	}
	else
	{
		HAL_UART_Transmit(&huart3, "pbuf not alloc!!", strlen("pbuf not alloc!!"), 10);
	}
	/*
	 * TCP/IP UDP communication END
	 */

    osDelay(100); // temporary 1 to 100, because of comport master print.. (nomally 1 is )
  }
  /* USER CODE END 5 */
}

LwIP.h

LwIP.h에다가 아래 코드 라인을 Includes for RTOS 주석 부분에 선언했다.

extern struct netif gnetif; 

udp_echoserver.c

통신으로 말미암아 발생하는 코드라인은 이 파일에 있는 아래 함수 쪽에 주로 작성하게 될 것이다.

void udp_echoserver_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{

  /* Connect to the remote client */
  udp_connect(upcb, addr, UDP_CLIENT_PORT);
    
  /* Tell the client that we have accepted it */
  udp_send(upcb, p);

  /* free the UDP connection, so we can accept new clients */
  udp_disconnect(upcb);
	
  /* Free the p buffer */
  pbuf_free(p);
   
}

리눅스 소켓 프로그래밍

리눅스 많이 쓰는 명령어

find / -name "kenGwon" -print
find ./ -name "kenGwon" -print

네트워크 소켓 프로그래밍





#include <sys/socket.h>
int socket(int domain, int type, int protocol); // 성공 시 파일 디스크립터(fd, file descripter), 실패 시 -1 반환
...
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen); // 성공 시 0, 실패 시 -1 반환
...
int listen(int sockfd, int backlog0; // 성공 시 0, 실패 시 -1 반환
...
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 성공 시 파일 디스크립터(fd, file descripter), 실패 시 -1 반환

소켓 프로그래밍 예시 (서버 측)

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>void error_handling(char *message);​

​

int main(int argc, char *argv[]){int serv_sock;int clnt_sock;​

​

struct sockaddr_in serv_addr;struct sockaddr_in clnt_addr;​

socklen_t clnt_addr_size;​

​

char message[]="Hello World!";​

​

if(argc!=2){printf("Usage : %s <port>\n", argv[0]);exit(1);}​

​

serv_sock=socket(PF_INET, SOCK_STREAM, 0);if(serv_sock == -1)error_handling("socket() error");​

​

memset(&serv_addr, 0, sizeof(serv_addr));​

serv_addr.sin_family=AF_INET;​

​
  ​

serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);​

serv_addr.sin_port=htons(atoi(argv[1]));​

​

if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1 )error_handling("bind() error"); ​

​

if(listen(serv_sock, 5)==-1)error_handling("listen() error");​

​

clnt_addr_size=sizeof(clnt_addr);  ​

clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size);if(clnt_sock==-1)error_handling("accept() error");  ​

​

write(clnt_sock, message, sizeof(message));close(clnt_sock);close(serv_sock);return 0;}​

​

void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);}

소켓 프로그래밍 예시 (클라이언트 측)

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>void error_handling(char *message);​

​

int main(int argc, char* argv[]){int sock;struct sockaddr_in serv_addr;char message[30];int str_len;​

​

if(argc!=3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}​

​

sock=socket(PF_INET, SOCK_STREAM, 0);if(sock == -1)error_handling("socket() error");​

​

memset(&serv_addr, 0, sizeof(serv_addr));​

serv_addr.sin_family=AF_INET;​

serv_addr.sin_addr.s_addr=inet_addr(argv[1]);​

serv_addr.sin_port=htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)error_handling("connect() error!");​

​

str_len=read(sock, message, sizeof(message)-1);if(str_len==-1)error_handling("read() error!");​

​

printf("Message from server: %s \n", message);close(sock);return 0;}​

​

void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);}

TCP & UDP

listen 하고, connect 요청하고, accept 하는 과정이
있으면 TCP... 없으면 UDP...

TCP

UDP

hercules로 UDP 패킷 송수신 체크하기

여기부터는 linux에서 진행

Qt 다운로드 링크

https://download.qt.io/archive/qt/5.12/
우리는 5.12.4 버전에서 리눅스용 "qt-opensource-linux-x64-5.12.4.run"를 다운로드 받았다.

임베디드에서 qt를 사용하는 이유는 OS를 타지 않기 때문이다. 다시 말해, OS호환성이 좋은 것이다.

OpenCV를 활용한 번호판 인식(text까지 conversion)

haarcascde russian plate number.xml

profile
스펀지맨

0개의 댓글