guileschool
受講者数
3,302
レビュー数
233
評価
4.7
인하대학교 공과대학에서 전자공학 학사 학위를 받았으며 임베디드 시스템용 소프트웨어 개발자로써 수년간의 경력을 쌓았습니다. 임베디드 시스템 및 프로그래밍을 위한 전문 강사로도 활동 중입니다. 아이폰 3GS 등장과 같은 시기에 맥(북)에 입문하였고, 그때부터 맥(북) 자동화에 관심을 갖게 되었습니다. '맥(북)에서 사용할 수 있는 시리 리모트', '키보드마에스트로를 이용한 구글번역기' 같은 오픈 소스를 깃허브(https://github.com/guileschool) 에 두고 개발 및 유지보수하고 있습니다. 오픈 소스 하드웨어 '비글본블랙' 에도 많은 관심을 가지고 있습니다. 맥(북) 자동화를 이용하여 생산성을 높일 수 있는 컴퓨터를 사용하는 모든 분야에 관심을 가지고 있고, 이를 필요로 하는 사람들을 돕고 있습니다.
강의 요청 및 기타 문의사항은 guileschool@gmail.com으로 보내주세요 :)
講義
受講レビュー
- FreeRTOSプログラミング
- FreeRTOSプログラミング
- ARM Cortex-M プロセッサのプログラミング
投稿
Q&A
포팅 부탁드립니다. <NUCLEO-G071RB>
안녕하세요. jykim3님!포팅 자료를 참고해주세요https://inf.run/9vCpB
- 0
- 2
- 17
Q&A
재진입가능여부에 관한 질문
안녕하세요. 박상우님!아래와 같이 사용하시면 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
- 29
Q&A
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
- 37
Q&A
그러면 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
- 34
Q&A
실행순서
안녕하세요. 박상우님!멀티태스킹 환경에서의 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
- 34
Q&A
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
- 28
Q&A
그림들도 해석 할 수 있어야 하나요?
안녕하세요. 이윤주님!CORTEX 패밀리 각각의 특징을 요약해드린것이고요, '꼭 이해하고 가야 하는 내용은 아니다' 라고 말씀드립니다. 더불어 이번 수업은 CORTEX-M 기반의 STM32 칩을 중심으로 진행됩니다.그러니 해당 주제에만 집중하셔도 좋습니다. 🙂
- 1
- 2
- 50
Q&A
스택오버플로우 실습 중 stack size 설정 질문
안녕하세요, SeongJin 님!영상 촬영 당시에는 스택 사이즈가 128 이었고, 이후 예제 튜토리얼이 버젼업되면서 스택 크기 여유분 증가의 필요성 때문에 수정하게 되었고 현재는 256 이 맞습니다. 추가적으로 궁금한 점 있으시면 다시 질문 남겨주세요. 감사합니다 🙂
- 0
- 2
- 31
Q&A
no-stlink
글쎄요.. 처음에 설치하시지 않았을까요?(사진)
- 1
- 2
- 45
Q&A
포팅 원합니다.
taegyu224님이 말씀하신 포팅 자료는 아래 링크에서 다운로드 하실 수 있습니다.https://inf.run/45f6Y혹여 사용하시는 컴파일러 버젼과 이 자료가 호환성 문제가 있을 수 있으니 그때는 아래 글을 참고해주세요.https://inf.run/9mVm4
- 2
- 2
- 49






