• 카테고리

    질문 & 답변
  • 세부 분야

    임베디드 · IoT

  • 해결 여부

    미해결

섹션 4 <소스코드 분석 - printf, fflush>1:52 부근에서 질문드릴 것이 있습니다!

23.10.17 20:20 작성 조회수 400

2

안녕하세요 강사님! 섹션 4 <소스코드 분석 - printf, fflush>1:52 부근에서 질문드릴 것이 있습니다! 강의에서 설명해주신 것을 저는 fflush() 를 주석하면 각 Task에서 UART 통신을 통해 전송한 문자들이 바로 flush되지 않고 버퍼에 쌓이다가 buffer 한계량을 초과할 때마다 출력이 되므로 우리가 생각했던대로인 'a'가 연속적으로 절반 출력되고, 'b'가 연속적으로 절반 출력되는 것이 반복되는 결과가 발생한다고 말씀하신 것으로 이해했습니다.

그러나 제가 이해가 안되는 것이 Task1, 2는 우선순위가 같고, 따라서 Task1 한 번 실행('a' 출력) -> Task2 한 번 실행('b' 출력) -> Task1 한 번 실행('a' 출력) ... 이 과정이 반복되어서 ababababababab가 출력되어야 하는 것이 아닌가요..??

 

답변 1

답변을 작성해보세요.

0

안녕하세요. madlife9161님!

타이머 하드웨어에서 발생하는 주기적인 인터럽트를 TICK 인터럽트라고 부릅니다. 이 인터럽트가 발생할 때마다 라운드 로빈(동일 우선 순위의 태스크 순환 실행)이 이루어집니다. 현재의 실습 예제에서는 이 주기를 1밀리초, 즉 HZ=1000으로 설정하였습니다. 동일 우선 순위의 태스크 T1과 T2가 있을 때, T1은 다른 인터럽트나 높은 우선순위의 태스크에 방해받지 않는 한, CPU를 1밀리초 동안 사용할 수 있습니다. 이 시간 동안 시리얼 출력 버퍼는 문자열로 채워질 것입니다. 1밀리초는 짧은 시간처럼 보일 수 있지만, 메모리 장치(버퍼)에는 상당한 양의 문자열을 저장할 수 있습니다.

아이스님의 프로필

아이스

2023.10.19

강사님 안녕하세요

틱인터럽트 기준 1ms동안 버퍼에 문자1000개가 들어가는 속도라 하고 버퍼는 100개까지 채울수 있다는 가정시

fflush함수가 없을 때는

a 100개가 1*10번

b 100개가 1*10번 번갈아 출력되는거죠? 출력으로 예측이됩니다. aaaabbbbaaaabbbb

그런데

fflush함수가 있으면

a 1개가 100*10번나오지 못하다 끝나고

b 1개가 100*10번나오지 못하다 끝나고출력되는데 이해가 안되요

말씀하신 uart로직이 결부되었으나

aaaaaabaaaaaabaaaaaab 이런 패턴은 마치 어느 한 태스크에만 영향을 받는것 처럼 보이는데

제가 질문한 예시로 답변 가능할까요

안녕하세요. 아이스님!

지금 질문하신 상황이 이런것으로 이해했습니다. 맞나요?

image

이런 상황은 코드가 다음과 같이 작성되어 있을 때 그렇습니다.

만약 테스트하셨던 환경이 아래 코드와 차이가 없다면, 이렇게 결과가 출력되는 것은 자연스런 상황으로 이해됩니다.

/*

* task.c

*

* Created on: Dec 22, 2020

* Author: YUNGKI HONG (guileschool@gmail.com)

* Copyright © 2015 guileschool

*/

/* FreeRTOS.org includes. */

#include "main.h"

#include "cmsis_os.h"

#include <stdio.h>

//#define CMSIS_OS

#define configUSE_PREEMPTION 1 // default '1'

#define configIDLE_SHOULD_YIELD 1 // default '1'

#define configUSE_TIME_SLICING 1 // default '1'

/* task's priority */

#define TASK_MAIN_PRIO 20

#define TASK_1_PRIO 10

#define TASK_2_PRIO 9

#define TASK_3_PRIO 8

struct Param_types { /* struct for parameter passing to task */

char *msg;

int P1,P2;

} Param_Tbl;

/* The task functions. */

static void TaskMain( void const *pvParameters );

static void Task1( void const *pvParameters );

static void Task2( const struct Param_types *Param );

#ifdef CMSIS_OS

osThreadId defaultTaskHandle;

osThreadId xHandleMain, xHandle1, xHandle2;

#else

TaskHandle_t xHandleMain, xHandle1, xHandle2;

#endif

int task1timer, task2timer;

/*-----------------------------------------------------------*/

void USER_THREADS( void )

{

/* Setup the hardware for use with the Beagleboard. */

//prvSetupHardware();

#ifdef CMSIS_OS

osThreadDef(defaultTask, TaskMain, osPriorityHigh, 0, 256);

defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

#else

/* Create one of the two tasks. */

xTaskCreate( (TaskFunction_t)TaskMain, /* Pointer to the function that implements the task. */

"TaskMain", /* Text name for the task. This is to facilitate debugging only. */

256, /* Stack depth - most small microcontrollers will use much less stack than this. */

NULL, /* We are not using the task parameter. */

TASK_MAIN_PRIO, /* This task will run at this priority */

&xHandleMain ); /* We are not using the task handle. */

#endif

}

static void TaskMain( void const *pvParameters )

{

const char *pcTaskName = "TaskMain";

struct Param_types *Param;

pvParameters = pvParameters; // for compiler warning

/* Print out the name of this task. */

printf( "%s is running\r\n", pcTaskName );

// TASK CREATE

/* TODO #1:

Task1을 생성

use 'xTaskCreate' */

#if 1

xTaskCreate( (TaskFunction_t)Task1, /* Pointer to the function that implements the task. */

"TaskMain", /* Text name for the task. This is to facilitate debugging only. */

256, /* Stack depth - most small microcontrollers will use much less stack than this. */

NULL, /* We are not using the task parameter. */

TASK_1_PRIO, /* This task will run at this priority */

&xHandle1 ); /* We are not using the task handle. */

#endif // TODO #1

/* Create the other task in exactly the same way. */

Param = &Param_Tbl; /* get parameter tbl addr */

Param->P1 = 111111; /* set parameter */

Param->P2 = 222222;

#ifdef CMSIS_OS

osThreadDef(Task2, (void const *)Task2, osPriorityBelowNormal, 0, 256);

xHandle2 = osThreadCreate (osThread(Task2), (void*)Param);

#else

xTaskCreate( (TaskFunction_t)Task2, "Task2", 256, (void*)Param, TASK_2_PRIO, &xHandle2 );

#endif

/* TODO #2:

Task1을 중지

use 'vTaskSuspend' */

#if 1

vTaskSuspend(xHandle1);

#endif // TODO #2

/* TODO #4:

Task1의 우선 순위를 'TASK_3_PRIO' 으로 변경

use 'vTaskPrioritySet' and 'vTaskResume' */

#if 1

vTaskPrioritySet(xHandle1, TASK_3_PRIO);

vTaskResume(xHandle1);

#endif // TODO #4

/* delete self task */

vTaskDelete (xHandleMain); // vTaskDelete (NULL);

}

static void Task1( void const *pvParameters )

{

const char *pcTaskName = "Task1";

pvParameters = pvParameters; // for compiler warning

/* Print out the name of this task. */

printf( "%s is running\n", pcTaskName );

printf("\n------- Task1 information -------\n");

printf("task1 name = %s \n",pcTaskGetName( xHandle1 ));

printf("task1 priority = %d \n",(int)uxTaskPriorityGet( xHandle1 ));

// printf("task1 status = %d \n",eTaskGetState( xHandle1 ));

printf("----------------------------------\n");

while(1) {

/* TODO #3:

코드를 실행 하여 보고

vTaskDelay() 코드를 주석 처리한 후 그 결과를 설명한다 */

#if 1 // No comment

vTaskDelay (pdMS_TO_TICKS (200));

printf("a"); fflush(stdout); // 문자 'a' 출력

#endif // TODO #3

task1timer++;

}

}

static void Task2( const struct Param_types *Param )

{

const char *pcTaskName = "Task2";

/* Print out the name of this task. */

printf( "%s is running\n", pcTaskName );

printf("\n------- Task2 parameter passed from main --------\n");

printf("task2 first parameter = %d \n",Param->P1);

printf("task2 second parameter = %d \n",Param->P2);

printf("--------------------------------------------------\n");

while(1) {

/* TODO #3:

코드를 실행 하여 보고

vTaskDelay() 코드를 주석 처리한 후 그 결과를 설명한다 */

#if 1 // No comment

vTaskDelay (pdMS_TO_TICKS (1000));

printf("b"); fflush(stdout); // 문자 'a' 출력

#endif // TODO #3

task2timer++;

}

}

/*-----------------------------------------------------------*/

그런데 말입니다. 아래 그림처럼 우선순위를 동일하게 한다고 해서 위의 출력 결과가 달라지지 않습니다.

#define TASK_1_PRIO 10

#define TASK_2_PRIO 10

 

두 개의 태스크(T1, T2)가 우선순위가 같다면 CPU 을 공평하게 사용합니다.

다만 T2가 T1 보다 CPU 시간을 덜 사용하게 되었다면 반드시 그 이유가 있을 것인데, 이번 사례에서처럼 T1 은 vTaskDelay (pdMS_TO_TICKS (200)); 에 의해 200밀리초만 휴면한 반면 T2 는 vTaskDelay (pdMS_TO_TICKS (1000)); 으로써 1초의 휴면시간을 갖게되었습니다.

 

이는 결과적으로 위의 printf 현상으로 나타나게 되었습니다.

 

잘 이해가 안되거나, 추가설명 필요하시면 다시 글 남겨주세요 ^^