printf("ho_tari\n");

3일차 본문

2025.03.27

 

오늘의 학습 목표

1. 과제 review

2. Memory 구조체 access

3. Debugger 사용

4. RTOS 기반 programming

 

#define PERIPH_BASE           0x40000000UL

#define GPIOB_BASE            (AHB1PERIPH_BASE + 0x0400UL)

#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000UL)

 

0x40000000 + 20000 + 400 → 0x40020400 (GPIOB 시작 주소)

 

typedef struct
{
  __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
  __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
  __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
  __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
  __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
  __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
  __IO uint32_t BSRR;     /*!< GPIO port bit set/reset register,      Address offset: 0x18      */
  __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
  __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
} GPIO_TypeDef;

 

__IO uint32_t MODER → 40020400

__IO uint32_t OTYPER → 40020404

__IO uint32_t OSPEEDR → 40020408

__IO uint32_t PUPDR → 4002040C

__IO uint32_t IDR 40020410

__IO uint32_t ODR → 40020414

...

 

구조체 pointer 멤버변수

GPIOB → ODR = 0x10;

(*GPIOB).ODR = 0x10;

 

포인터

- 주소를 저장하는 변수

 

포인터를 쓰는 이유

- 서로 다른 함수에 위치한 변수를 access하기 위함 (전역 변수 사용을 최소화)

 

선언할 때 : 주소를 저장하는 공간을 알려줌

사용할 때 : 주소의 내용을 찾아감

 

구조체 pointer member 사용 LED CONTROL

<led.c>

#include "led.h"

void led_main(void)
{
	while(1)
	{
//		(*GPIOB).ODR |= GPIO_PIN_0; // LED가 ON
//		GPIOB->ODR ^= GPIO_PIN_1; // LED1 toggle 반전
//		HAL_Delay(500);
//		GPIOB->ODR &= ~GPIO_PIN_0; // LED가 OFF
//		HAL_Delay(500);

		led_all_on();
		HAL_Delay(500);
		led_all_off();
		HAL_Delay(500);

		shift_left_led_on();
		shift_right_led_on();
		shift_left_keep_ledon();
		shift_right_keep_ledon();
		flower_on();
		flower_off();
	}
}

void led_all_on(void)
{
#if 1 // 구조체 pointer member
	GPIOB->ODR = 0xff;
#endif
#if 0 // DMA
	// printf("int %d\n", sizeof(int)); // 4로 찍히는지 확인
	*(unsigned int *)GPIOB_ODR = 0xff;
#endif
#if 0 // HAL
//	HAL_GPIO_WritePin(GPIOB, 0xff, 1);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
			GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7, 1);
#endif
}

void led_all_off(void)
{
#if 1 // 구조체 pointer member
	GPIOB->ODR = 0x00;
#endif
#if 0 // DMA
	// printf("int %d\n", sizeof(int)); // 4로 찍히는지 확인
	*(unsigned int *)GPIOB_ODR = 0x00;
#endif
#if 0 // HAL
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
			GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7, 0);
#endif
}

void shift_left_led_on(void)
{
#if 1 // 구조체 pointer member
	for(int i = 0; i < 8; i++)
	{
		GPIOB->ODR = 0x01 << i;
		HAL_Delay(100);
		led_all_off();
	}
	HAL_Delay(100);
#endif
#if 0 // DMA
	for(int i = 0; i < 8; i++)
	{
		*(unsigned int*)GPIOB_ODR = 0x01 << i;
		HAL_Delay(100);
		led_all_off();
	}
	HAL_Delay(100);
#endif
#if 0 // HAL
	uint16_t ledpins[8] = {
			GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
			GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7
	};

	for(int i = 0; i < 8; i++)
	{
		HAL_GPIO_WritePin(GPIOB, ledpins[i], 1);
		HAL_Delay(100);
		led_all_off();
	}
	HAL_Delay(100);
#endif
}

void shift_right_led_on(void)
{
#if 1 // 구조체 pointer member
	for(int i = 0; i < 8; i++)
	{
		GPIOB->ODR = 0x80 >> i;
		HAL_Delay(100);
		led_all_off();
	}
	HAL_Delay(100);
#endif
#if 0 // DMA
	for(int i = 0; i < 8; i++)
	{
		*(unsigned int*)GPIOB_ODR = 0x80 >> i;
		HAL_Delay(100);
		led_all_off();
	}
	HAL_Delay(100);
#endif
#if 0 // HAL
	uint16_t ledpins[8] = {
			GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
			GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7
	};

	for(int i = 7; i >= 0; i--)
	{
		HAL_GPIO_WritePin(GPIOB, ledpins[i], 1);
		HAL_Delay(100);
		led_all_off();
	}
	HAL_Delay(100);
#endif
}

void shift_left_keep_ledon(void)
{
#if 1 // 구조체 pointer member
	for(int i = 0; i < 8; i++)
	{
		GPIOB->ODR |= 0x01 << i;
		HAL_Delay(100);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // DMA
	for(int i = 0; i < 8; i++)
	{
		*(unsigned int*)GPIOB_ODR |= 0x01 << i;
		HAL_Delay(100);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // HAL
	uint16_t ledpins[8] = {
			GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
			GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7
	};

	for(int i = 0; i < 8; i++)
	{
		HAL_GPIO_WritePin(GPIOB, ledpins[i], 1);
		HAL_Delay(100);
	}
	led_all_off();
	HAL_Delay(100);
#endif
}

void shift_right_keep_ledon(void)
{
#if 1 // 구조체 pointer member
	for(int i = 0; i < 8; i++)
	{
		GPIOB->ODR |= 0x80 >> i;
		HAL_Delay(100);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // DMA
	for(int i = 0; i < 8; i++)
	{
		*(unsigned int*)GPIOB_ODR |= 0x80 >> i;
		HAL_Delay(100);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // HAL
	uint16_t ledpins[8] = {
			GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
			GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7
	};

	for(int i = 7; i >= 0; i--)
	{
		HAL_GPIO_WritePin(GPIOB, ledpins[i], 1);
		HAL_Delay(100);
	}
	led_all_off();
	HAL_Delay(100);
#endif
}

void flower_on(void)
{
#if 1 // 구조체 pointer member
	for(int i = 0; i < 4; i++)
	{
		GPIOB->ODR |= 0x10 << i | 0x08 >> i;
		HAL_Delay(150);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // DMA
	for(int i = 0; i < 4; i++)
	{
		*(unsigned int*)GPIOB_ODR |= 0x10 << i | 0x08 >> i;
		HAL_Delay(150);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // HAL
	uint16_t ledpins[8] = {
			GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
			GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7
	};

	for(int i = 4; i >= 0; i--)
	{
		HAL_GPIO_WritePin(GPIOB, ledpins[i] | ledpins[7 - i], 1);
		HAL_Delay(150);
	}
	led_all_off();
	HAL_Delay(100);
#endif
}

void flower_off(void)
{
#if 1 // 구조체 pointer member
	led_all_on();
	HAL_Delay(100);
	for(int i = 4; i >= 0; i--)
	{
		GPIOB->ODR &= ~(0x10 << i | 0x08 >> i);
		HAL_Delay(150);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // DMA
	led_all_on();
	HAL_Delay(100);
	for(int i = 4; i >= 0; i--)
	{
		*(unsigned int*)GPIOB_ODR &= ~(0x10 << i | 0x08 >> i);
		HAL_Delay(150);
	}
	led_all_off();
	HAL_Delay(100);
#endif
#if 0 // HAL
	uint16_t ledpins[8] = {
			GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
			GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7
	};

	led_all_on();
	HAL_Delay(100);

	for(int i = 0; i < 4; i++)
	{
		HAL_GPIO_WritePin(GPIOB, ledpins[i] | ledpins[7 - i], 0);
		HAL_Delay(150);
	}

	HAL_Delay(100);
#endif
}

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <string.h>

int unicode_to_utf8(uint32_t codepoint, char* utf8)
{
	// 1바이트: 0xxxxxxx (ASCII)
	if (codepoint <= 0b01111111)
	{
		utf8[0] = (char)codepoint;
		return 1;
	}
	// 2바이트: 110xxxxx 10xxxxxx
	else if (codepoint <= 0b11111111111)
	{
		utf8[0] = (char)(0b11000000 | ((codepoint >> 6) & 0b00011111));
		utf8[1] = (char)(0b10000000 | (codepoint & 0b00011111));
		return 2;
	}
	// 3바이트: 1110xxxx 10xxxxxx 10xxxxxx
	else if (codepoint <= 0b1111111111111111)
	{
		utf8[0] = (char)(0b11100000 | ((codepoint >> 12) & 0b11100000));
		utf8[1] = (char)(0b11100000 | ((codepoint >> 6) & 0b11100000));
		utf8[2] = (char)(0b11100000 | (codepoint & 0b00111111));
		return 3;
	}
	// 4바이트: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
	else if (codepoint <= 0b100001111111111111111)
	{
		utf8[0] = (char)(0b11110000 | ((codepoint >> 18) & 0b00000111));
		utf8[1] = (char)(0b10000000 | ((codepoint >> 12) & 0b00111111));
		utf8[2] = (char)(0b10000000 | ((codepoint >> 6) & 0b00111111));
		utf8[3] = (char)(0b10000000 | (codepoint & 0b00111111));
		return 4;
	}
	return 0;
}

int main(void) 
{
	uint32_t name[] = { 0xBC15, 0xC131, 0xD638 };
	char utf8_name[20] = { 0 };
	int pos = 0;

	for (int i = 0; i < 3; i++) {
		pos += unicode_to_utf8(name[i], utf8_name + pos);
	}
	utf8_name[pos] = '\0';

	printf("UTF-8 인코딩 결과 : ");
	for (int i = 0; i < pos; i++) {
		printf("%02X ", (unsigned char)utf8_name[i]);
	}

	return 0;
}

 

<실행 결과>

 

OS

- 운영체제 또는 오퍼레이팅 시스템

- 응용 프로그램이 요청하는 시스템 잦원을 효율적으로 분배하고  관리

- 시스템 자원을 효율적으로 관리하며 사용자가 컴퓨터를 편리하고 효과적으로 사용할 수 있도록 환경을 제공하는  여러 프로그램의 모임

- 사용자와 하드웨어간의 인터페이스로서 동작하는 시스템 소프트웨어

- 대표적인 운영체제로는 Windows, Mac, UNIX, LINUX가 있다.

 

실시간 운영체제

- 특정한 짧은 시간 내에 이벤트나 데이터의 처리를 보증하는 운영체제

- 싱글태스킹일 수도 있고 멀티태스킹일 수도 있으며 멀티태스킹의 경우 특수한 스케줄링 알고리즘을 사용

 

RTOS 특징

- 멀티태스킹을 지원 : 여러 개의 task를 동시에 동작시킬 수 있다. 각 task마다 우선 순위를 둘 수 있다.

- 짧은 interrupt latency : interrupt latency는 실제 인터럽트가 발생한 후 인터럽트 핸들러까지 도착하는 시간

- 작은 kernal size

- 유명한 RTOS : VxWorks, pSOS, Nucles

 

FreeRTOS

- 15년 동안 세계 유수의 칩 회사들과 협력하여 개발된 FreeRTOS는 현재 170초마다 다운로드되는 업계 최고의 마이크로컨트롤러 및 소형 마이크로프로세서용 실시간 운영 체제 (RTOS)

- MIT 오픈 소스 라이선스에 따라 무료로 배포되는 FreeRTOS에는 모든 산업 분야에서 사용할 수 있는 커널과 라이브러리 세트가 포함

- FreeRTOS는 오픈 소스 프로젝트 : https://github.com/FreeRTOS/FreeRTOS 사이트에서 소스코드를 다운로드하거나 변경 또는 개선 사항에 기여하거나 문제를 보고할 수 있다.

- MIT 오픈 소스 라이선스에 따라 FreeRTOS 코드를 릴리스하므로 상업 및 개인 프로젝트에서 사용할 수 있다.

 

FreeRTOS 소스 코드 다운로드

- www.freertos.org의 페이지에서 최신 FreeRTOS 및 장기 Support (LTS) 패키지 다운로드

 

시스템 자원 (System Resource)

- 컴퓨터 하드웨어와 같은 개념으로 CPU, 메모리, 입출력 장치, 저장 매체 등 시스템에서 사용할 수 있는 자원을 의미한다. 시스템 자원은 스스로 메모리 확보, 저장 위치, 저장 방법 등을 직접 결정할 수 없기 때문에 반드기 운영체제가 필요하다.

 

응용 프로그램

- 운영체제를 제외한 나머지 소프트웨어로 엑셀,  파워포인트, 엑세스 등을 사용자가 평소에 사용하는 프로그램을 의미한다.

- 운영체제는 응용 프로그램이 요청하는 메모리를 허가하고 분배

- 운영체제는 응용 프로그램이 요청하는 CPU 시간을 제공

- 운영체제는 응용 프로그램이 요청하는 입출력 장치  사용 여부를 허가 및 제어

- 운영체제에서 실행되는 모든 프로그램은 운영체제에 종속적일 수 밖에 없다.

 

커널

- 운영체제는 규모가 매우 큰 프로그램이므로 운영체제의 모든 부분을 메모리에 올려놓는 것은 메모리의 측명으로 봤을 때 굉장히 비효율적이다. 따라서, 운영체제는 필요한 부분만을 메모리에 올려서 사용하게 되는데 이 때 메모리에 상주하는 운영체제의 핵심부분을 커널이라고 한다.

- 커널은 메모리에 상주하는 부분이므로 운영체제의 핵심 부분이라고 볼  수 있기 때문에 주로 운영체제 = 커널이라고 한다.

 

시스템 콜

- 사용자는 운영체제의 기능을 담당하는 커널에 직접 접근할 수가 없다. 따라서 사용자와 커널 사이에 인터페이스 역할이 필요한데 시스템 콜리 바로 이 역할을 하게  된다.

- 사용자가 커널 영역을 사용할 수 있게 즉 응용프로그램이 시스템 자원에 직접 접근하여 필요한 기능을 사용할 수 있게 해주는 함수를 의미한다.

- 하지만 보통은 응용프로그램은 시스템 콜을 직접 사용하지 않고 해당 시스템 콜을 사용하여 만든 언어 별 라이브러리 API를 통해 커널에 접근할 수 있다.

 

process

- 실행파일을 클릭했을 때 메모리(RAM) 할당이 이루어지고 이 메모리공간으로 코드가 올라간다. 이 순간부터 이 프로그램은 process라 불리게 된다. 실행 중인 program을 process라고 한다.

 

task

- 일의 실행하는 최소 기본 단위, 프로세스와 쓰레드 의미

 

프로세스의 스케줄링

- CPU는 하나인데 동시에 여러 프로세스가 실행되어야 한다.

- CPU는 여러 개의 프로세스를 번갈아가면서 실행하는데 매우 고속이기 때문에  우리 눈에는 동시에 실행되는 것처럼 보인다.

- 이러한 멀티프로세스 운영체제에서 프로세스의 CPU 할당 순서 및 방법을 결정짓는 것을 스케줄링이라 한다.

 

라운드 로빈 스케줄링

- RR 알고리즘은 선점(preemptive)형 알고리즘으로 사분할 시스템을 위해 설계되었다.

- FCFS스케줄링과 비슷하지만 시스템이 프로세스를 사이를 옮겨 다닐 수 있도록 선점이 추가된다.

- 시간 할당량 또는 time slice라는 시간의 단위를 정의하여 구현한다.

- 어떤 프로세스가  이 단위 시간을 지나면 실행을 강제로 다른 프로세스에게 선점시키는 것이다.

- 일반적으로 time quantum은 10에서 100밀리초 동안이다.

 

선점형 알고리즘과 비선점형 알고리즘의 차이점

- 선점형 알고리즘은 CPU를 강제로 빼앗을 수 있어 시스템의 응답성을 높이는 반면, 컨텍스트 스위칭에 따른 오버헤드와 복잡성이 존재한다.

- 비선점형 알고리즘은 단순하고 오버헤드가 적지만, 한 프로세스가 CPU를 독점하게 되어 응답성이 떨어질 수 있는 단점이 있습니다.

 

비선점형 스케줄링

- (실행 → 대기), (실행 → 종료)로의 상태전이가 있을 때 적용

 

선점형 스케줄링

- (실행 → 대기), (대기 → 준비), (수행 → 종료) 모든 상태변화에서 적용

 

비선점 스케줄링

- 이미 할당된 CPU를 다른 프로세스가 강제로 빼앗아 사용할 수 없는 스케줄링 기법

- 스케줄러 호출 빈도가 낮고 문맥 교환에 의한 오버헤드도 적음

- 일괄 처리 시스템에 적합하고, CPU 사용시간이 긴 하나의 프로세스가 CPU 사용시간이 짧은 여러 프로세스를 오랫동안 대기시킬 수 있으므로 처리율이 떨어질 수 있다.

 

선점 스케줄링

- 하나의 프로세스가 CPU를 할당 받아 실행하고 있을 때 우선 순위가 높은 다른 프로세스가 CPU를 강제로 빼앗아 사용할 수 있는 기법

- 모든 프로세스에세 CPU 사용시간을 동일하게 부여할 수 있으며 빠른 응답시간을 요하는 대화식 시분할 시스템에 적합하며 긴급한 프로세서를 제어할 수 있다.

 

컨텍스트 스위칭

- 문맥 교환이란 하나의 프로세스가 CPU를 사용중인 상태에서 다른 프로세스가 CPU를 사용하도록 하기 위해 이전의 프로세스의 상태(문맥)를 보관하고 새로운 프로세스의 상태를 적재하는 작업을 말한다.

 

컨텍스트 스위칭의 필요 이유

- 여러 프로세스 / 스레드를 동시에 실행시키기 위해서

 

컨텍스트 스위칭이 발생하는 시점

- 주어진 time slice를 다 사용했거나

-  I/O 작업을 해야하거나

- 다른 리소스 (다른 프로세스나 스레드에서 사용하고 있는)를 기다려야 하거나

 

thread context switching

 

 프로세스와 쓰레드

- 메모리 구조 관점에서 본 프로세스와 쓰레드

- 자식 프로세스가 생성되고  난 다음에는 부모 프로세스가 가진 핸들테이블은 상속되지만 모든 것이  독립적으로  만들어진다. 이런한 메모리 구조를 지녔기에 프로세스 간에 데이터를 메모리 구조를 지녔기에 IPC(Inter Process Communication)가 필요하다.

- 쓰레드를 생성하는 경우 메모리 구조는 다르다.

- 프로세스가 쓰레드 A와 B를 생성한 경우 쓰레드를 생성할 때마다 해당 쓰레드만을 위한 ThreadStack 영역 (실행 흐름의 추가를 위한 최소조건)만이 생성될 뿐 나머지 영역은 부모 프로세스 영역에 공유하고 있다. 

- 쓰레드마다 스택을 독립적으로 할당해준다.

 

쓰레드

<main.c>

/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for myTask02 */
osThreadId_t myTask02Handle;
const osThreadAttr_t myTask02_attributes = {
  .name = "myTask02",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for myTask03 */
osThreadId_t myTask03Handle;
const osThreadAttr_t myTask03_attributes = {
  .name = "myTask03",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};

void StartDefaultTask(void *argument);
void StartTask02(void *argument);
void StartTask03(void *argument);

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of myTask02 */
  myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);

  /* creation of myTask03 */
  myTask03Handle = osThreadNew(StartTask03, NULL, &myTask03_attributes);

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	osDelay(50);
  }
  /* USER CODE END 5 */
}

void StartTask02(void *argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
	button_led_toggle_test();
    osDelay(1);
  }
  /* USER CODE END StartTask02 */
}

void StartTask03(void *argument)
{
  /* USER CODE BEGIN StartTask03 */
  /* Infinite loop */
  for(;;)
  {
	osDelay(1);
  }
  /* USER CODE END StartTask03 */
}

'(Telechips) AI 시스템 반도체 SW 개발자 교육 > STM32CubeIDE' 카테고리의 다른 글

6일차  (0) 2025.04.01
5일차  (0) 2025.03.31
4일차  (0) 2025.03.28
2일차  (0) 2025.03.26
1일차  (0) 2025.03.25