본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 42일차 - ARM & RTOS 활용(Endian, Review, Mission)

by Graffitio 2023. 8. 25.
[Harman 세미콘 아카데미] 42일차 - ARM & RTOS 활용(Endian, Review, Mission)
728x90
반응형
[Endian]

 

Endian이란?

컴퓨터 메모리와 같은 데티어 저장 방식을 의미한다.

컴퓨터 아키텍쳐마다 데이터 메모리를 저장하는 방식이 다르기 때문에 Endian이라는 개념이 나오게 되었다.

엔디언은 데이터의 byte 순서를 나타내며, 크게 두 가지 종류로 나뉜다.(Big Endian / Little Endian)

 

Endian의 중요성은 주로 다른 시스템 간 데이터 교환 시 발생한다.

잘 다루지는 않지만, 통신, ADC과 같이 변환이 필요한 작업에서 종종 사용되기 때문에 이해하고 있어야 된다.


 

Big Endian

 

Big Endian은 MSB부터 시작하여 LSB까지 데이터를 나열하는 방식이다.

즉, 중요한 부분이 앞에 위치하고 순서대로 나열하며, 인간이 사용하는 수 나열법과 같다.

주로 네트워크 프로토콜에서 사용된다.

 

ex)

0x1245678을 4byte big Endian으로 표현

Result : 12  34  56  78

 


 

Little Endian

 

Little Endian은 LSB부터 시작하여 MSB까지 데이터를 나열하는 방식이다.

즉, 데이터의 끝에 가까운 부분이 앞에 위치하게 되고 대대수의 컴퓨터 아키텍쳐에서 사용되며,

x86 아키텍쳐와 호환성이 높다.

 

ex)

0x1245678을 4byte Little Endian으로 표현

Result : 78  56  34  12

 


 

[Review]

 

UART ADC Review

 

[Harman 세미콘 아카데미] 37일차 - ARM & RTOS 활용(UART & ADC 활용)

[UART] UART : 비동기 직렬 통신 과거에는 속도가 1,200 bps(≒baudrate)정도였기에, k(10^3 ≒ 2^10), M(10^6 ≒ 2^20), G(10^9 ≒ 2^30), T(10^12 ≒ 2^40), P(10^15 ≒ 2^50) m(mili), u(micro), n(nano), p(pico) 와 같이 데이터 크기

rangvest.tistory.com

 

    UART module을 통해 문자를 전송하는 역할을 한다.(시리얼 포트로 1 byte씩 내보내도록 리디렉션)

    UART 함수는 실제로 실체가 없기 때문에 console 함수를 통해 실체화시켜줘야 한다.

    STM 보드에는 모니터와 같은 출력 콘솔과 연결할 수 있는 단자가 없기에, 시리얼 터미널로 리디렉션해줘야 한다.

    이렇게 리디렉션 해줌으로써, printf 함수(string 출력 함수) 등을 사용할 수 있게 된다.

 

int __io_putchar(int ch);
{
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
}

필요한 Argument check

        &huart : UART Port에 대한 handle(포인터)

        &ch :전송할 버퍼의 Data pointer

        1 :전송할 버퍼의 Data size

        100 : Timeout : 정상적으로 통신이 이루어지지 않았다는 것을 판단을 할 수 있는 시간

                 9600 ≒ 1k byte / sec = 1 byte / 1ms

 

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
	printf("Hello! This is STM32\r\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  HAL_Delay(200);
  }
  /* USER CODE END 3 */
}

※ PuTTY 를 사용할 경우에는 개행문자(\r, \n 등)도 같이 작성해주어야 출력된다.

 


 

PULLL-UP / PULL-DOWN 저항 설정법

 

코드 수정으로도 바꿀 수 있으나, ioc파일에서도 변경이 가능하다.

 


 

JoyStick 활용

 

/* USER CODE BEGIN 0 */

// UART Redirection
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
}

// Read X, Y, Z convert to digital
void Joystick_Test()
{
	HAL_ADC_Start(&hadc1); // xVal ADC Sampling Start(Rank1)
	HAL_ADC_PollForConversion(&hadc1, 100);
	int xVal = HAL_ADC_GetValue(&hadc1); // Read Value

	HAL_ADC_Start(&hadc1); // yVal ADC Sampling Start(Rank2)
	HAL_ADC_PollForConversion(&hadc1, 100);
	int yVal = HAL_ADC_GetValue(&hadc1); // Read Value

	int zVal = HAL_GPIO_ReadPin(Z_Value_GPIO_Port, Z_Value_Pin);

	printf("(x, y, z) = (%d, %d, %d)\r\n", xVal, yVal, ~zVal+2);
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
int count = 0;
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_ADC1_Init();
  /* USER CODE BEGIN 2 */
	printf("Hello! STM32 Application Start.\r\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  Joystick_Test();
	  HAL_Delay(100);
  }
  /* USER CODE END 3 */
}

 


 

Interrupt 활용

 

Interrupt Review

 

[Harman 세미콘 아카데미] 38일차 - ARM & RTOS 활용(ADC, Interrupt, Timer)

[ADC 활용] ADC Parameter Setting 하나의 채널 사용 시에는 별도의 세팅이 필요없지만, 여러 개의 채널을 사용할 때는 Parameter setting에 신경을 좀 써줘야 한다. Clock Configuration Clock Prescaler 84MHz ÷ 4분주 = 2

rangvest.tistory.com

 

Interrupt 를 사용하기 위해서 필요한 것이 하나 있다.

Interrupt Vector Table

일종의 주소록이라 생각하면 된다.

각 인터럽트들이 우선 순위에 따라서 설명과 함께 기록되어 있다.

 

Joystick을 Interrupt 활용하여 출력하는 코드를 작성해보자.

우리는 ADC1만 사용하여 두 개의 Channel을 입력받아서 사용해야되기 때문에

각 입력 사이에 대기 시간을 주어야 한다.

HAL_ADC_PollForConversion(&hadc1, 100); 를 사용하여 대기 시간을 추가해준다.

 

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
int xVal, yVal, zVal;
int cnt;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

// UART Redirection
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
}

// ISR function
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	switch(cnt)
	{
		case 1 :
			HAL_ADC_PollForConversion(&hadc1, 100);
			int xVal = HAL_ADC_GetValue(&hadc1); // Read x_Value
			break;

		case 2 :
			HAL_ADC_PollForConversion(&hadc1, 100);
			int yVal = HAL_ADC_GetValue(&hadc1); // Read y_Value
			break;

		case 3 :
			int zVal = HAL_GPIO_ReadPin(Z_Value_GPIO_Port, Z_Value_Pin); // Read z_Value
			break;

		case 4 :
			printf("(x, y, z) = (%d, %d, %d)\r\n", xVal, yVal, ~zVal+2);
			break;

		default :
			cnt = 0;
			break;
	}
	cnt++;
	HAL_ADC_Start_IT(&hadc1); // Restart(1 time interrupt enable command)
}

/* USER CODE END 0 */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  int count = 0;
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_ADC1_Init();

  HAL_ADC_Start_IT(&hadc1); // ISR Start
  /* USER CODE BEGIN 2 */
	printf("Hello! STM32 Application Start.\r\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

ISR로 실행되기 때문에 출력 속도가 매우 빠르다.

    tip)

      컨트롤을 누르고 함수를 클릭하면, 해당 함수가 선언된 부분으로 이동시켜준다.

 

 

[Mission]

 

Mission

 

Q. 조이스틱의 포지션을 출력하도록 코딩하여라

    ex) x가 좌측, y가 전방, z가 push 포지션을 취할 경우,

         Status : [Front, Left] <Push> 출력

 

Result

 

1. Header Coding

/* USER CODE BEGIN PV */
int xVal, yVal, zVal; // 전역변수로 선언해야 메인 함수에서도 사용 가능하다.
int cnt; // ISR에서 사용되는 카운터 변수
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

// UART Redirection
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart2, &ch, 1, 100);
    return ch; // 이걸 추가해줘야 warning이 안 뜸
}

// ISR function
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	switch(cnt)
	{
		case 1 :
			HAL_ADC_PollForConversion(&hadc1, 100); // 100ms 대기시간(그 전에 변환되면 넘어감)
			xVal = HAL_ADC_GetValue(&hadc1)-2048; // Read x_Value 그리고 xVal에 저장
			break;
		case 2 :
			HAL_ADC_PollForConversion(&hadc1, 100);
			yVal = HAL_ADC_GetValue(&hadc1)-2048; // Read y_Value
			break;
		case 3 :
			zVal = HAL_GPIO_ReadPin(Z_Value_GPIO_Port, Z_Value_Pin); // Read z_Value
			break;
		default :
			cnt = 0;
			break;
	}
	cnt++;
	HAL_ADC_Start_IT(&hadc1); // Restart(1 time interrupt enable command)
}

 

2. main()

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  int x = 0; // x 좌표값을 저장하는 변수 초기화
  int y = 0; // y 좌표값을 저장하는 변수 초기화
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_ADC1_Init();

  HAL_ADC_Start_IT(&hadc1); // ISR Start
  /* USER CODE BEGIN 2 */
	printf("Hello! STM32 Application Start.\r\n"); // 시작 메시지 출력
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      // 조이스틱 값을 해석하여 x, y 값을 설정
      // xVal, yVal은 이전에 전역 변수로 업데이트되었음을 전제로 함
	  if(xVal > 30) x = 2;
	  else if((xVal > -70) && (xVal < 30))  x = 1;
	  else x = 0;

	  if(yVal > 30) y = 2;
	  else if((yVal > -70) && (yVal < 30))  y = 1;
	  else y = 0;
      
      // 조이스틱 버튼이 눌렸을 때,
	  if ((~zVal+2) == 1) // 왜 그런지는 모르겠는데, zVal은 1 0, ~zVal이 -1 -2로 출력됨. 그래서 그냥 이렇게 썼음.
	  {
		  switch(y)
		  {
		  	  case 0 :
		  		  if(x == 0) printf("Status : [Back, Left  ] <Push>\r\n");
				  else if (x == 1) printf("Status : [Back,       ] <Push>\r\n");
				  else printf("Status : [Back, Right ] <Push>\r\n");
				  break;
		  	  case 1 :
		  		  if(x == 0) printf("Status : [Left        ] <Push>\r\n");
				  else if (x == 1) printf("Status : [Middle      ] <Push>\r\n");
				  else printf("Status : [Right       ] <Push>\r\n");
				  break;
		  	  case 2 :
		  		  if(x == 0) printf("Status : [Front, Left ] <Push>\r\n");
				  else if (x == 1) printf("Status : [Front,      ] <Push>\r\n");
				  else printf("Status : [Front, Right] <Push>\r\n");
		  }
	  }
	  else
	  {
		  switch(y)
		  {
		  	  case 0 :
		  		  if(x == 0) printf("Status : [Back, Left  ]\r\n");
				  else if (x == 1) printf("Status : [Back        ]\r\n");
				  else printf("Status : [Back, Right ]\r\n");
				  break;
		  	  case 1 :
		  		  if(x == 0) printf("Status : [Left        ]\r\n");
				  else if (x == 1) printf("Status : [Middle      ]\r\n");
				  else printf("Status : [Right       ]\r\n");
				  break;
		  	  case 2 :
		  		  if(x == 0) printf("Status : [Front, Left ]\r\n");
				  else if (x == 1) printf("Status : [Front       ]\r\n");
				  else printf("Status : [Front, Right]\r\n");
		  }
	  }
	  HAL_Delay(100);
  }
  /* USER CODE END 3 */
}

조이스틱의 포지션에 따라 값이 출력된다.

728x90
반응형