CubeMX 설정
클라이언트가 서버에게 1초마다 한번씩 데이터를 전송하게끔 하기 위해 TIM1을 쓸 예정이다. TIM1은 APB2에서 클락소스를 공급받는다.
TIM1의 update interrupt를 enable한다.
아래 헤더파일과 소스파일을 포함한다.
main.c
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tcpClientRAW.h"
/* USER CODE END Includes */
// ...
/* USER CODE BEGIN PV */
extern struct netif gnetif;
/* USER CODE END PV */
// ...
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART3_UART_Init();
MX_RTC_Init();
MX_LWIP_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim1);
tcp_client_init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
MX_LWIP_Process();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
코드는 서버 때와 비슷하다. 단 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 함수가 호출되도록 HAL_TIM_Base_Start_IT(&htim1); 해준다.
void tcp_client_init(void) 함수
/* IMPLEMENTATION FOR TCP CLIENT
1. Create TCP block.
2. connect to the server
3. start communicating
*/
void tcp_client_init(void)
{
/* 1. create new tcp pcb */
struct tcp_pcb *tpcb;
tpcb = tcp_new();
/* 2. Connect to the server */
ip_addr_t destIPADDR;
IP_ADDR4(&destIPADDR, 192, 168, 0, 11);
tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected);
}
TCP 블락을 만들고 목적지 IP와 포트 no. 를 파라미터로 tcp_connect() 함수를 통해 서버와 연결한다.
static err_t tcp_client_connected(void *arg, struct tcp_pcb *newpcb, err_t err) 함수
/** This callback is called, when the client is connected to the server
* Here we will initialise few other callbacks
* and in the end, call the client handle function
*/
static err_t tcp_client_connected(void *arg, struct tcp_pcb *newpcb, err_t err)
{
err_t ret_err;
struct tcp_client_struct *es;
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
/* allocate structure es to maintain tcp connection information */
es = (struct tcp_client_struct *)mem_malloc(sizeof(struct tcp_client_struct));
if (es != NULL)
{
es->state = ES_CONNECTED;
es->pcb = newpcb;
es->retries = 0;
es->p = NULL;
/* pass newly allocated es structure as argument to newpcb */
tcp_arg(newpcb, es);
/* initialize lwip tcp_recv callback function for newpcb */
tcp_recv(newpcb, tcp_client_recv);
/* initialize lwip tcp_poll callback function for newpcb */
tcp_poll(newpcb, tcp_client_poll, 0);
/* initialize LwIP tcp_sent callback function */
tcp_sent(newpcb, tcp_client_sent);
/* handle the TCP data */
tcp_client_handle(newpcb, es);
ret_err = ERR_OK;
}
else
{
/* close tcp connection */
tcp_client_connection_close(newpcb, es);
/* return memory error */
ret_err = ERR_MEM;
}
return ret_err;
}
클라이언트가 서버와 연결되면 이 함수가 호출되고 또 몇몇 콜백함수를 지정한다.
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) 함수
/** This callback is called, when the client receives some data from the server
* if the data received is valid, we will handle the data in the client handle function
*/
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
struct tcp_client_struct *es;
err_t ret_err;
LWIP_ASSERT("arg != NULL",arg != NULL);
es = (struct tcp_client_struct *)arg;
/* if we receive an empty tcp frame from server => close connection */
if (p == NULL)
{
/* remote host closed connection */
es->state = ES_CLOSING;
if(es->p == NULL)
{
/* we're done sending, close connection */
tcp_client_connection_close(tpcb, es);
}
else
{
/* we're not done yet */
// /* acknowledge received packet */
// tcp_sent(tpcb, tcp_client_sent);
/* send remaining data*/
// tcp_client_send(tpcb, es);
}
ret_err = ERR_OK;
}
/* else : a non empty frame was received from server but for some reason err != ERR_OK */
else if(err != ERR_OK)
{
/* free received pbuf*/
if (p != NULL)
{
es->p = NULL;
pbuf_free(p);
}
ret_err = err;
}
else if(es->state == ES_CONNECTED)
{
/* store reference to incoming pbuf (chain) */
es->p = p;
// tcp_sent has already been initialized in the beginning.
// /* initialize LwIP tcp_sent callback function */
// tcp_sent(tpcb, tcp_client_sent);
/* Acknowledge the received data */
tcp_recved(tpcb, p->tot_len);
/* handle the received data */
tcp_client_handle(tpcb, es);
pbuf_free(p);
ret_err = ERR_OK;
}
else if(es->state == ES_CLOSING)
{
/* odd case, remote side closing twice, trash data */
tcp_recved(tpcb, p->tot_len);
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
else
{
/* unknown es->state, trash data */
tcp_recved(tpcb, p->tot_len);
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
return ret_err;
}
서버로 부터 데이터를 수신하면 콜백되는 함수다. ES_CONNECTED 상태일 때는 해당 분기로 들어와서 수신한 데이터를 필요에 따른 처리를 하고 수신했음을 확인한다.
int counter = 0;
// ...
/* Handle the incoming TCP Data */
static void tcp_client_handle (struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{
/* get the Remote IP */
ip4_addr_t inIP = tpcb->remote_ip;
uint16_t inPort = tpcb->remote_port;
/* Extract the IP */
char *remIP = ipaddr_ntoa(&inIP);
// esTx->state = es->state;
// esTx->pcb = es->pcb;
// esTx->p = es->p;
esTx = es;
pcbTx = tpcb;
counter++;
}
여기선 수신 데이터를 따로 처리는 안하고 전역으로 선언된 counter 변수를 ++ 한다. 그리고 전역 구조체 변수인 exTx와 pcbTx에 파라미터로 들어온값을 대입한다.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 함수
extern TIM_HandleTypeDef htim1;
/* create a struct to store data */
struct tcp_client_struct *esTx = 0;
struct tcp_pcb *pcbTx = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
char buf[100];
/* Prepare the first message to send to the server */
int len = sprintf (buf, "Sending TCPclient Message %d\n", counter);
if (counter !=0)
{
/* allocate pbuf */
esTx->p = pbuf_alloc(PBUF_TRANSPORT, len , PBUF_POOL);
/* copy data to pbuf */
pbuf_take(esTx->p, (char*)buf, len);
tcp_client_send(pcbTx, esTx);
pbuf_free(esTx->p);
}
}
1초마다 콜백되게 설정. pbuf_alloc()함수로 pbuf를 동적할당하고 pbuf_take로 데이터를 pbuf에 카피한다. 그리고 tcp_client_send() 함수를 사용에 서버로 데이터를 전송한 후 할당한 pbuf는 pbuf_free로 해제해준다.
tcp_client_send 함수
/** A function to send the data to the server
*/
static void tcp_client_send(struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{
struct pbuf *ptr;
err_t wr_err = ERR_OK;
while ((wr_err == ERR_OK) &&
(es->p != NULL) &&
(es->p->len <= tcp_sndbuf(tpcb)))
{
/* get pointer on pbuf from es structure */
ptr = es->p;
/* enqueue data for transmission */
wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
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
{
/* other problem ?? */
}
}
}
'임베디드 개발 > STM32 (ARM Cortex-M)' 카테고리의 다른 글
STM32 ] lwIP 통계와 디버그 기능 활성화하기 (LWIP_STATS, LWIP_DEBUG) (1) | 2022.11.11 |
---|---|
STM32 ] UDP Client, lwIP Raw API (20) | 2022.10.03 |
STM32 ] TCP Server, lwIP Raw API (13) | 2022.10.02 |
STM32 ] lwIP UDP Echo Sever, 속도 테스트 (0) | 2022.09.25 |
STM32 ] UDP Server, lwIP Raw API (0) | 2022.09.25 |