Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

임베디드 소프트웨어/Zephyr

[nRF52840 + Zephyr] #13. Inter-task communication B

방피터 2023. 6. 4. 23:22

2023.06.03 - [임베디드 소프트웨어/Zephyr] - [nRF52840 + Zephyr] #11. Inter-task communication A

 

[nRF52840 + Zephyr] #11. Inter-task communication A

제퍼의 경우에는 Inter-thread communication이 되겠구만! 지원되는 기능을 살펴보니.... 너무 많아;;;😢 👇👇 우린 너무 많은 거 안좋아 하니까 줄여보자구. 난 보통 다른 RTOS에서는 Flag와 Queue를 많이

engschool.tistory.com

이전에서 FIFO랑 Queue만 해봤는데

이번에는 Message Queue랑, Mail box를 해볼거야.

 

그럼 Message Queue부터 시작!

Message Queue의 기본적인 동작은 그냥 Queue와 다를 바 없어.

차이점은 Queue가 포인터만 전달했다면

Message Queue는 고정된 크기의 데이터를 전달한다는 거지.

바로 예제 ㄱㄱ

#include <zephyr/kernel.h>

#define MY_STACK_SIZE 500
#define MY_PRIORITY 5

typedef struct  {
	uint8_t field1;
	uint8_t field2;
}msgq_data_t;

K_MSGQ_DEFINE(my_msgq, sizeof(msgq_data_t), 10, 4);

void my_entry_point(void *, void *, void *){
	int ret;
	msgq_data_t msgq_data;
	for(;;){
		k_sleep(K_MSEC(1000));
		ret = k_msgq_get(&my_msgq, &msgq_data, K_FOREVER);
		printk("data1: %d,data2: %d\n",msgq_data.field1, msgq_data.field2);
	}
}

K_THREAD_DEFINE(my_tid, MY_STACK_SIZE,
                my_entry_point, NULL, NULL, NULL,
                MY_PRIORITY, 0, 0);

void main(void)
{
	msgq_data_t msgq_data;
	uint8_t data1 = 0,data2= 127;
	int ret;
	for(;;){
		msgq_data.field1 = data1++;
		msgq_data.field2 = data2++;
		k_sleep(K_MSEC(500));
		ret = k_msgq_put(&my_msgq, &msgq_data, K_NO_WAIT);
		if(ret){
			printk("msgq is full. Purge all messages.\n");
			k_msgq_purge(&my_msgq);
		} else {
			printk("Sucesefully queued: %d,%d\n",msgq_data.field1, msgq_data.field2);
		}
	}
}

main에서 계속 500ms 단위로 message queue에 넣고

다른 쓰레드에서 1초 마다 읽어봐서 message queue에서 읽어오는 예제야.

읽어오는 속도보다 넣는 속도가 더 빠르니 당연히 queue가 가득차서 못 넣겠지.

그때 purge 시켜버렸어. queue를 다 비우는 거지 ㅎ

큐가 가득 차면 purge 시킴

다음은 mail box?

mailbox도 기본적으로 Queue!

그런데 ISR(interrupt service routine)에서는 사용할 수 없어.

데이터 크기가 고정일 필요도 없고 타겟 thread를 지정할 수도 있어.

약간 진보된 Queue 느낌이랄까?

 

바로 예제 고고

#include <zephyr/kernel.h>

#define MY_STACK_SIZE 500
#define MY_PRIORITY 5

K_MBOX_DEFINE(my_mbox);//mail box 선언

void my_entry_point(void *, void *, void *){
	int ret;
	struct k_mbox_msg rx_msg;//rx_msg
	for(;;){
		rx_msg.rx_source_thread = K_ANY;//아무한테서나 받는다!
		k_mbox_get(&my_mbox, &rx_msg, NULL, K_FOREVER);//메일 올때까지 기다림!
		printk("Received message: %d\n", rx_msg.info);
	}
}

K_THREAD_DEFINE(my_tid, MY_STACK_SIZE,
                my_entry_point, NULL, NULL, NULL,
                MY_PRIORITY, 0, 0);

void main(void)
{
	int ret;
	struct k_mbox_msg tx_msg;
	uint8_t info = 0;
	for(;;){
		tx_msg.info = info++;
		tx_msg.size = 0;
		tx_msg.tx_data = 0;
		tx_msg.tx_block.data = NULL;
		tx_msg.tx_target_thread = K_ANY;//아무나 받아라!
		k_mbox_put(&my_mbox,&tx_msg, K_FOREVER);//메일 받을때까지 기다림!
		k_sleep(K_MSEC(1000));
	}
}

첫번째 maibox 예제는 비어있는 메일을 보내는 거야.

그래서 오직 k_mbox_msg 구조체 중에 info값만 의미가 있을거야.

이걸 보니 zephyr에 flag가 없는게 이해가 감. mailbox를 쓰면 됨!

아래는 결과인데 info만 출력하도록 해놨어.

info 출력

info 말고 긴 데이터도 보내보자구.

#include <zephyr/kernel.h>

#define MY_STACK_SIZE 500
#define MY_PRIORITY 5

K_MBOX_DEFINE(my_mbox);

void my_entry_point(void *, void *, void *){
	int ret;
	char buffer[100];// 100개 짜리 버퍼~
	struct k_mbox_msg rx_msg;
	for(;;){
		rx_msg.rx_source_thread = K_ANY;//아무한테서나 받아왔!
		rx_msg.size=100;
		k_mbox_get(&my_mbox, &rx_msg, buffer, K_FOREVER); // 메일 기다렷!
		printk("Received message: %s\n", buffer);//출력
	}
}

K_THREAD_DEFINE(my_tid, MY_STACK_SIZE,
                my_entry_point, NULL, NULL, NULL,
                MY_PRIORITY, 0, 0);

void main(void)
{
	int ret;
	char buffer[100];
	int size;
	struct k_mbox_msg tx_msg;
	uint8_t info = 0;
	for(;;){
		size = sprintf(buffer, "어머니는 짜장면이 싫다고 하셨어. 탕수육이 좋다고 하셨어~ 이야야~", NULL);
		tx_msg.info = info++;
		tx_msg.size = size;
		tx_msg.tx_data = buffer; //전송할 데이터
		tx_msg.tx_block.data = NULL;
		tx_msg.tx_target_thread = K_ANY;// 아무한테나 줘버렷!
		k_mbox_put(&my_mbox,&tx_msg, K_FOREVER); //받을 때까지 기다렷!
		k_sleep(K_MSEC(1000));
	}
}

char 100개짜리 버퍼를 만들고 임의의 데이터를 넣고 전송해봤어.

당연한 말이지만 잘 전송되고 잘 받아서 출력해.

👇👇

인코딩 문제인지 vscode 터미널에서는 한글이 조금씩 깨져서

nRF connect 프로그램으로 터미널 실행했음.

 

마지막으로 아주 큰 block 데이터를 전송하는 기능이 있는데 음...

딱히 두 번째 예제와 다를 바가 없을 듯. 그래서 과감하게 생략!

해보고 싶은 사람들은 아래 링크를 참고하도록!

https://docs.zephyrproject.org/3.1.0/kernel/services/data_passing/mailboxes.html

 

Mailboxes — Zephyr Project Documentation

A thread receives a message by first creating a message descriptor that characterizes the message it wants to receive. It then calls one of the mailbox receive APIs. The mailbox searches its send queue and takes the message from the first compatible thread

docs.zephyrproject.org

 

mailbox는 타겟을 지정할 수 있다는 점에서 실제 프로젝트에서 꽤 유용할 듯 해.

물론 mailbox를 쓸 수 있을 만큼 thread 갯수가 많아야 겠지만 ㅎㅎ

원래는 pipe도 같이 해보려고 했는데;;;

너무 글이 길어져서 pipe는 다음 시간에!! ㅋㅋ

안녕!

👋👋👋

반응형