홍영기
@guileschool
수강생
3,389
수강평
241
강의 평점
4.7
게시글
질문&답변
[소스코드 분석-configUSE_TIME_SLICING] TASK1,2의 우선순위가 동일할 때, configUSE_TIME_SLICING값 변경에 따른 출력 변화
안녕하세요. 이준형님!아마도 우선순위를 동일하게 주고 테스트하신 것 같네요. 맞나요?"누가 먼저 CPU를 잡느냐"는 항상 Task1으로 동일하고, "누가 먼저 a/b를 출력하느냐"는 각 태스크가 시작할 때 시간이 많이 소요될 수 있는 printf의 수량과 tick 타이밍이 언제 발생하느냐에 따라 달라집니다.다시 말하면, 논리적으로는 비슷한 형태의 태스크 함수가 주어져 있고 Task1이 먼저 생성되니 'a'를 먼저 출력할 것이라 생각하기 쉽지만, tick이 발생하는 시점과 선행하는 printf 출력 시간이 합쳐져서 'b'가 먼저 출력되는 구조가 만들어질 수 있습니다.아래와 같이 시나리오를 그려보았습니다. 참고해보세요.## TIME_SLICING = 1 인 경우Task1 시작 -> 시작 메시지 printf 실행 중...Tick Interrupt 발생!TIME_SLICING=1이므로 같은 우선순위 태스크 체크 -> Task2 있음 -> 전환!Task2 실행 -> 시작 메시지 출력 시작이후 tick마다 Task1 Task2 교대결과적으로 Task2가 while문의 'b'를 Task1보다 먼저 찍게 되는 상황 발생## TIME_SLICING = 0 인 경우Task1 시작 -> 시작 메시지 printf 실행 중...Tick Interrupt 발생!TIME_SLICING=0이므로 같은 우선순위 체크 코드 실행 안 됨 -> 전환 없음!Task1 계속 실행 -> while문 진입 -> "aaaaaaa..."Task2는 yield나 block 없이는 실행 기회 없음정리하면, 질문자님께서 관찰한 "TIME_SLICING 값에 따라 먼저 출력되는 문자가 다르다"는 현상은 CPU 선점 순서가 바뀐 게 아니라, tick 발생 시점과 시작 코드 실행 시간의 조합으로 인해 while문 진입 순서가 달라진 것입니다.
- 0
- 2
- 32
질문&답변
DMA 관련한 강의도 제작해주실 수 있으신가요 ?
안녕하세요. yilee님!강의 재밌게 잘 보고 계시나요?DMA 강의에 대해 문의 주셨는데요, 아쉽게도 이번 강의 내용에서는 다루지 않습니다.FREERTOS 도 마찬가지고요.임베디드 시스템에서 고성능 처리를 위해서 중요한 기능인 DMA 에 대해서 다음 강의에서 다뤄볼게요.좋은 의견 감사합니다. 즐거운 하루되시고요 ^^
- 0
- 1
- 38
질문&답변
포팅 부탁드립니다. <NUCLEO-G071RB>
안녕하세요. jykim3님!포팅 자료를 참고해주세요https://inf.run/9vCpB
- 0
- 2
- 43
질문&답변
재진입가능여부에 관한 질문
안녕하세요. 박상우님!아래와 같이 사용하시면 void swap(int x, int y) 은 재진입에 대응할 수 있는 함수로서의 기능을 잘 수행해줍니다.// 태스크 1int a = 10, b = 20;swap(&a, &b); // temp1은 태스크1의 스택// 태스크 2 (동시 실행)int c = 30, d = 40;swap(&c, &d); // temp2는 태스크2의 스택하지만, 아래와 같은 사용 예시에서는 재진입이 안된다는 점 양지하시기 바랍니다.이건 swap 함수가 재진입 불가능해서가 아니라, 호출자가 같은 데이터에 동시 접근했기 때문입니다.// 태스크 1swap(&shared_x, &shared_y);// 태스크 2 (동시 실행)swap(&shared_x, &shared_y);- 끝 -
- 1
- 1
- 41
질문&답변
01_TASKMAN프로젝트 디버깅 모드 실패
안녕하세요. 학습자님!STM32CubeIDE 최신 버전에서 GCC 컴파일러가 업데이트되면서 발생하는 문제가 의심되어 chatGPT 에 확인한 결과 예상대로 그 문제가 맞았습니다.__FILENAME__ 매크로가 더 이상 기본 제공되지 않습니다.__FILE_NAME__ 을 사용하시면 되겠습니다. 아래 내용 참고해주세요.컴파일러 옵션으로 매크로 정의 (가장 깔끔)1. 프로젝트 우클릭 → Properties2. C/C++ Build → Settings → Tool Settings3. MCU GCC Compiler → Preprocessor4. Defined symbols (-D) 에 추가:```FILENAME=__FILE_NAME__```
- 1
- 2
- 49
질문&답변
그러면 malloc/free가 아닌 동적할당자를 써서 메모리를 할당했기떄문에
안녕하세요. 박상우님!필요하다면 main 함수 시작 직후 c runtime heap 영역에 malloc 이나 new 로 큰 메모리 블럭을 할당하시고 그 영역을 bss 에 할당하고 있는 것처럼 freertos 용 동적 메모리 풀을 선언하여 사용하실 수 있습니다. // FreeRTOSConfig.h #define configAPPLICATION_ALLOCATED_HEAP 1 #define configTOTAL_HEAP_SIZE (100 * 1024) // main.c uint8_t *ucHeap; // extern으로 선언된 ucHeap을 위해 int main(void) { // C 런타임 heap에서 할당 ucHeap = (uint8_t*)malloc(configTOTAL_HEAP_SIZE); if (ucHeap == NULL) { // 할당 실패 처리 while(1); } // 이제 FreeRTOS는 malloc된 영역을 ucHeap으로 사용 xTaskCreate(...); vTaskStartScheduler(); return 0; }
- 1
- 2
- 46
질문&답변
실행순서
안녕하세요. 박상우님!멀티태스킹 환경에서의 printf 디버깅은 자칫하면 태스크의 동작 상황에 대해 잘못 이해하는 오류를 범하기 쉬운데요. 그런 의미에서 지금 하신 질문과 궁금증은 그 의미가 크다고 할 수 있습니다.답변 드리기 앞서 제가 수행한 테스트와 그 결과를 지금부터 봐주시죠.테스트에 사용한 코드는 다음과 같습니다. 박상우님이 궁금해 하시는 그 조건이죠.저는 로직분석기를 이용해서 태스크 동작을 면밀히 보기 위해 필요한 코드를 추가하였습니다.HAL_GPIO_WritePin(GPIOB, T1_Pin, GPIO_PIN_SET); 바로 이거입니다.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"); //vTaskDelay(1); while(1) { HAL_GPIO_WritePin(GPIOB, T1_Pin, GPIO_PIN_SET); /* TODO #3: 코드를 실행 하여 보고 vTaskDelay() 코드를 주석 처리한 후 그 결과를 설명한다 */ #if 1 // No comment //vTaskDelay (pdMS_TO_TICKS (1000)); printf("a"); fflush(stdout); // 문자 'a' 출력 #endif // TODO #3 HAL_GPIO_WritePin(GPIOB, T1_Pin, GPIO_PIN_SET); 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) { HAL_GPIO_WritePin(GPIOB, T2_Pin, GPIO_PIN_SET); int i; /* TODO #3: 코드를 실행 하여 보고 vTaskDelay() 코드를 주석 처리한 후 그 결과를 설명한다 */ #if 1 // No comment //vTaskDelay (pdMS_TO_TICKS (1000)); printf("b"); fflush(stdout); // 문자 'b' 출력 #endif // TODO #3 HAL_GPIO_WritePin(GPIOB, T2_Pin, GPIO_PIN_SET); task2timer++; } } 아래 실행결과를 보시죠. 정확하게 우선순위를 동일하게 부여한 Task1 와 Task2 은 printf, fflush, HAL_UART_Transmit 가 실행되고 있는지 여부와 관계없이 정확하게 1밀리 초 간격으로 라운드로빈이 수행되는 것을 볼 수 있습니다.(사진) (사진)또한 아래와 같은 테스트 결과도 보시겠습니다. 단지 171 라인의 실행 유무에 따라 아래와 같이 printf 출력 결과는 크게 달라집니다.(사진)(사진)(사진) 이 증상은 어떻게 설명할 수 있는가 하면 규칙적으로 발생하는 tick 인터럽트와 주기적(일정한 간격)으로 실행되는 for 루프를 고려하면 어떤 태스크는 운이좋아 콘솔 출력버퍼에 글자를 더 많이 입력할 수 있게 되고 또 다른 어떤 태스크는 콘솔 출력버퍼에 글자를 덜 입력하게 되는 상황으로 이해할 수 있을 겁니다. 아래 함수 HAL_UART_Transmit 내에서는 UART 전송을 위해 버퍼가 비워지는 때까지 CPU 시간을 소모하면서 기다리는 코드(UART_WaitOnFlagUntilTimeout) 가 사용되고 있는데, 이 함수가 바로 지금 이러한 이상한 일이 일어나는 주 원인일 것입니다. 절묘한 타이밍 차이에 따라 task1 이나 task2 둘 중 하나는 이 쓸모없는? 일만 수행하게 되었을 겁니다. 어떤 태스크는 a (혹은 b) 글자를 버퍼에 넣는 생산적인 일을 위주로 실행하는가 하면, 어떤 태스크는 이 기다리는 일을 위주로 수행 하는 거죠.HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { uint8_t *pdata8bits; uint16_t *pdata16bits; uint32_t tickstart = 0U; /* Check that a Tx process is not already ongoing */ if (huart->gState == HAL_UART_STATE_READY) { if ((pData == NULL) || (Size == 0U)) { return HAL_ERROR; } /* Process Locked */ __HAL_LOCK(huart); huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; /* Init tickstart for timeout management */ tickstart = HAL_GetTick(); huart->TxXferSize = Size; huart->TxXferCount = Size; /* In case of 9bits/No Parity transfer, pData needs to be handled as a uint16_t pointer */ if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)) { pdata8bits = NULL; pdata16bits = (uint16_t *) pData; } else { pdata8bits = pData; pdata16bits = NULL; } /* Process Unlocked */ __HAL_UNLOCK(huart); while (huart->TxXferCount > 0U) { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } if (pdata8bits == NULL) { huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU); pdata16bits++; } else { huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU); pdata8bits++; } huart->TxXferCount--; } if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } /* At end of Tx process, restore huart->gState to Ready */ huart->gState = HAL_UART_STATE_READY; return HAL_OK; } else { return HAL_BUSY; } } 따라서, 개발과 디버깅에 있어 언제든 이런 상황이 일어나고 있다는 점을 고려해야 할겁니다.그리고, 추가적으로 아래 소스도 참고해보시기 바랍니다.### 1. printf.chttps://github.com/lattera/glibc/blob/master/stdio-common/printf.c### 2. vfprintf.chttps://github.com/lattera/glibc/blob/master/stdio-common/vfprintf.c### 3. fileops.c (new_do_write, IOnew_file_write 등)https://github.com/lattera/glibc/blob/master/libio/fileops.chttps://github.com/bminor/glibc/blob/master/libio/fileops.c### 4. syscall.S (x86_64 어셈블리)https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/x86_64/syscall.S### 5. iofflush.chttps://github.com/lattera/glibc/blob/master/libio/iofflush.c
- 1
- 2
- 45
질문&답변
uart 전송중에는 스위칭이 금지되나요?
안녕하세요. 박상우님!freertos 수업중에 사용한 예제 소스 기준으로 답변드립니다. 현재 우리 소스 코드에서는 stm32cubeide 에서 기본으로 제공하는 HAL API UART 드라이버를 이용하여 printf 을 수행합니다.결국 질문 내용은 관련 핵심 함수인 HAL_UART_Transmit 을 살펴보아야 할텐데요. 해당 함수내에서 tick 인터럽트를 불활성하거나 작동을 방해할 만한 요소는 발견되지 않습니다. 따라서, 간단히 답변드리자면 'fflush 나 HAL_UART_Transmit 함수가 실행중이더라도 tick 인터럽트 이벤트가 발생한다면 인터럽트는 즉시 처리된다. 그리고 문맥전환도 필요하다면 실행된다' 입니다.HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { uint8_t *pdata8bits; uint16_t *pdata16bits; uint32_t tickstart = 0U; /* Check that a Tx process is not already ongoing */ if (huart->gState == HAL_UART_STATE_READY) { if ((pData == NULL) || (Size == 0U)) { return HAL_ERROR; } /* Process Locked */ __HAL_LOCK(huart); huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; /* Init tickstart for timeout management */ tickstart = HAL_GetTick(); huart->TxXferSize = Size; huart->TxXferCount = Size; /* In case of 9bits/No Parity transfer, pData needs to be handled as a uint16_t pointer */ if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)) { pdata8bits = NULL; pdata16bits = (uint16_t *) pData; } else { pdata8bits = pData; pdata16bits = NULL; } /* Process Unlocked */ __HAL_UNLOCK(huart); while (huart->TxXferCount > 0U) { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } if (pdata8bits == NULL) { huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU); pdata16bits++; } else { huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU); pdata8bits++; } huart->TxXferCount--; } if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } /* At end of Tx process, restore huart->gState to Ready */ huart->gState = HAL_UART_STATE_READY; return HAL_OK; } else { return HAL_BUSY; } }
- 1
- 2
- 45
질문&답변
그림들도 해석 할 수 있어야 하나요?
안녕하세요. 이윤주님!CORTEX 패밀리 각각의 특징을 요약해드린것이고요, '꼭 이해하고 가야 하는 내용은 아니다' 라고 말씀드립니다. 더불어 이번 수업은 CORTEX-M 기반의 STM32 칩을 중심으로 진행됩니다.그러니 해당 주제에만 집중하셔도 좋습니다. 🙂
- 1
- 2
- 69
질문&답변
스택오버플로우 실습 중 stack size 설정 질문
안녕하세요, SeongJin 님!영상 촬영 당시에는 스택 사이즈가 128 이었고, 이후 예제 튜토리얼이 버젼업되면서 스택 크기 여유분 증가의 필요성 때문에 수정하게 되었고 현재는 256 이 맞습니다. 추가적으로 궁금한 점 있으시면 다시 질문 남겨주세요. 감사합니다 🙂
- 0
- 2
- 52




