본문 바로가기
Embedded

FreeRTOS와 Semaphore, Queue를 이용한 시리얼 통신

by gigasound 2021. 10. 24.

 


FreeRTOS

RTOS(real time Operating System)는 프로세스와 주변 자원을 공유하거나 동작 여부를 보장해주는 방법들의 집단입니다. 여기에는 thread를 기본으로 task를 구성하는 방법과, 자원을 공유하는 뮤텍스 세마포어등이 기본으로 들어갑니다.

향후에 이에 관한 세부 내용을 정리하겠습니다.

이글에서는 RTOS 중에서 마이크로프로세서에 많이 사용되는 FreeRTOS를 이용해서 Arduin 에서 시리얼 통신을 구현하는 방법을 알아보겠습니다.


Arduino의 FreeRTOS

Arduino에서 FreeRTOS를 이용해서 시리얼 수신하는 방법을 알아보겠습니다. 우선 다른 글에서 다루었던 Queue 생성 내용을 먼저 참조해야 합니다. 

먼저 아두이노에서 라이브러리 매니저에서 FreeRTOS를 선택해서 설치합니다.

그리고 다음과 같이 필요한 헤더를 포함합니다.

#include <Arduino.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
//- queue
#include "myQueue.h"

#define STACK_SIZE (configMINIMAL_STACK_SIZE+256)
//- 수신 제어를 위한 세마포어
SemaphoreHandle_t rx0_sem;
//- 수신 문장을 전송
void Task_Rx0(void *par);

수신용 queue를 버퍼를 만듭니다.

//- uart1d의 수신용 queue 객체 , 312 이상의 버퍼를 사용하면 문제가 발생함 
myQueue rx0_msg_buff = myQueue(312);

시리얼 통신에서 송신을 위해서 _printf를 사용하면 편리합니다. 이에 관한 내용은 아래 내용을 참조해서 코드를 추가해 주면 좋습니다.

https://studymake.tistory.com/696

다음과 같이 setup()과 loop()을 설정합니다. 

void setup() {
	//-- uart
	//- serial 통신 속도 
	Serial.begin(9600);
	_printf(uart0,"\r\nDasp88Neo Start..\r\n");
	//- semaphore 생성
	rx0_sem = xSemaphoreCreateCounting(8,0);
	//- thread 생성 
	xTaskCreate(Task_Rx0,"Rx0_Handler",STACK_SIZE, NULL,2,NULL);
	//- thread 실행
	vTaskStartScheduler();
	//- 아래 문장이 실행되면 thread가 실행되지 않음을 의미 
	for(;;){
	}  
}

/* 
rtos를 사용하면, loop에 아무런 내용도 없어야한다.
 */
void loop() {
}

시리얼 수신 이벤트

시리얼 통신 수신 이벤트에 Queue를 이용해서 수신 데이터를 기록하고, 원하는 문자(RX_LF)를 정의한 다음에 이 문자가 수신되면 세마포어를 이용해서 이를 해독하는 함수가 진행되로 독 유도합니다.

/******************************************************************************
 * uart0 receive interrupt
 * 수신용 queue와 세마포아를 사용함  
 */  
void serialEvent(){ 
	char c;
	//- 높은 순위의 task 여부 확인용 
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;  
	//- 수신 문자가 있을 때 까지 무한 대기
	while(Serial.available()){
		//- 수신 기록용 버퍼의 용량을 제한 
		if(rx0_msg_buff.count()<rx0_msg_buff.size()){
			//- 한개의 문자를 수신
			c = Serial.read();
			//- 문자를 queue에 기록
			rx0_msg_buff.push(c);
			//- 수신 문자가 메세지 완료이면 해석을 시작, Tx0_Rx를 시행하도록 유도
			if(c == RX_LF){
				xSemaphoreGiveFromISR(rx0_sem,&xHigherPriorityTaskWoken);
			}
			//- 높은 우선 순위의 task에 대응 
			if(xHigherPriorityTaskWoken==pdTRUE){
				taskYIELD();
			}
		}
	}
}

스레드가 세마포어 상태를 수신해서, 기록된 queue의 버퍼 내용을 수신하고 해석하는 함수 Rx_Receive()를 동작합니다. Rx_Receive()는 사용자가 설정해야 합니다.

/******************************************************************************
 * 	thread로 동작한다. 
 *	uart0로 수신된 내용을 처리한다. 
 *  uart_info 객체에 수신 명령어를 기록 
 *
 *  par: 의미 없음, thread를위한 함수 형태임  
 */
void Task_Rx0(void *par){
	//- 세마포의 실행 대기 시간, 충분히 긴 사간을 사용해도 된다.
	const TickType_t block_time = pdMS_TO_TICKS(200);
	//- task를 무한히 실행
	for(;;){
		//- 세마포어의 수신 대기
		if(xSemaphoreTake(rx0_sem,block_time)==pdTRUE){
			//- queue에 있는 수신 문장을 Rx_Receive()에 한 문자씩 전송
			while(rx0_msg_buff.count()>0){
				Rx_Receive((char)rx0_msg_buff.pop());
			}
		}
	}
}

광고좀 꾹 눌러주시면 고맙겠습니다. 


위의 내용을 참조용으로만 사용해주세요. 무단 도용이나 무단 복제는 불허합니다.

기타 문의 사항은 gigasound@naver.com에 남겨 주시면 고맙겠습니다.