반응형

https://youtube.com/live/CZO7eE0qtEg

[STM32#05] 로터리엔코더(rotary encoder)를 작동시키는 방법에 대해서 알아보고 GPIO 외부인터럽트(EXTI)를 활용해보기!(녹칸다 내맘대로 STM32)

심심한녹칸다의 내맘대로 STM32시리즈이다!

STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g39d2b1dd9a3_1_0#slide=id.g39d2b1dd9a3_1_0

이번편에는 녹칸다의 STM32쉴드에 붙어있는 로터리엔코더를 작동시켜보도록 합니다~!

 

로터리엔코더 회로도

(STM32쉴드회로도)

(직접연결할떄)

 

(로터리엔코더의 인식원리)

1.일단 기본적으로 로터리엔코더는 시계방향으로 회전하냐? 반시계방향으로 회전하냐? 를 알수있다!
2.회전방향과 몇스탭을 이동했는지? 스탭량을 알 수 있다!
3.시계방향으로 회전할때와 반시계방향으로 회전할때의 신호값이 다르게된다!
4.시계방향으로 1스탭 회전을 하게되면 A핀에 상승엣지가 발생하고 그때 B의 값은 LOW가 된다!
5.반시계방향으로 1스탭 회전을 하게되면 A핀에 상승엣지가 발생하고 그때 B의 값은 HIGH가 된다!

 

 

(예제목차)

1.PB1에 상승엣지와 하강엣지를 이용해서 버튼이 눌려졌는지를 검출해서 시리얼출력하시오!

/* USER CODE BEGIN 2 */
 //버튼을 안누르고 있을때가 기본상태이고 안누르고 있으면 HIGH신호가 나온다
 uint8_t old = 1;
 char * msg1 = "PB1 PRESSED!!\n";
 char * msg2 = "PB1 RELEASED!!\n";
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	  //PB1의 하강엣지 검출(버튼 눌러짐)
	  //PB1의 현재 값을 측정한다!
	  uint8_t now = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
	  //now와 old가 공존하는 상태가 된다!
	  if(old == 1 && now == 0){
		  //이전값은 HIGH인데, 현재값은 LOW인 상황!(하강엣지)
		  HAL_UART_Transmit(&huart2, msg1, strlen(msg1), 100);
	  }else if(old == 0 && now == 1){
		  //이전값은 LOW이고, 현재값은 HIGH이다!(상승엣지)
		  HAL_UART_Transmit(&huart2, msg2, strlen(msg2), 100);
	  }
	  old = now;
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


2.인터럽트를 위해서 함수에 대해서 기본적으로 예제로 표현해보시오!

/* USER CODE BEGIN 0 */
char *msg1 = "NOCKANDA\n";
char buff[100];
//입력도 없고 출력도 없는 함수
void function1(){
	//이함수가 호출되면 usart2로 "NOCKANDA\n"를 출력한다!
	HAL_UART_Transmit(&huart2, msg1, strlen(msg1), 100);
}
//입력이 있고 출력은 없는 함수
void function2(int num){
	//숫자를 1개 입력받아서 그 값을 출력하는 함수!
	//함수의 입력으로 들어온 값은 지역변수처럼 쓸수있다!
	sprintf(buff,"NUMBER=%d\n",num);
	HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
}
//입력이 없고 출력이 있는 함수
int function3(){
	//호출하면 5678이라는 값을 반환(return)하는 함수!
	return 5678;
}
//입력과 출력이 둘다 있는 함수
int function4(int num1, int num2){
	//num1과 num2를 더해서 반환하는 함수
	int output = num1+num2;
	return output;
}
/* USER CODE END 0 */

//중략

while (1)
 {
	  function1(); //함수를 호출한다(=콜한다)
	  function2(1234);
	  int test_num = function3();
	  sprintf(buff,"function3=%d\n",test_num);
	  HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
	  int test_num2 = function4(111,222);
	  sprintf(buff,"function4=%d\n",test_num2);
	  HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
	  HAL_Delay(2000);
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


3.PB1을 IOC에서 외부인터럽트로 등록해서 버튼을 눌렀을때 눌려졌다는 문구를 시리얼출력하시오!(EXTI1)

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	//EXTI1
  if(GPIO_Pin == GPIO_PIN_1)
  {
	   HAL_UART_Transmit(&huart2, "PB1 INTERRUPTS!\n", 16, 100);
  }
}
/* USER CODE END 0 */

////중략

while (1)
 {
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


4.로터리엔코더를 폴링방식으로 처리해서 시계방향으로 회전하냐, 반시계방향으로 회전하냐, 스위치를 눌렀냐를 판별해서 시리얼출력하시오!

/* USER CODE BEGIN 2 */
 uint8_t old_A = 0;
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	  //녹칸다가 풀업방식으로 연결해놓음
	  uint8_t sw = HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7);
	  if(sw == 0){
		  HAL_UART_Transmit(&huart2, "SW\n", 3, 100);
		  HAL_Delay(200);
	  }
	  //PA11의 상승엣지가 발생했을때
	  uint8_t now_A = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
	  if(old_A == 0 && now_A == 1){
		  //PA12의 값을 측정해서 LOW면 시계, HIGH면 반시계
		  uint8_t now_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
		  if(now_B){
			 //반시계
			  HAL_UART_Transmit(&huart2, "CCW\n", 4, 100);
		  }else{
			 //시계
			  HAL_UART_Transmit(&huart2, "CW\n", 3, 100);
		  }
	  }
	  old_A = now_A;
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


5.녹칸다가 로터리엔코더를 돌렸을때 시계방향으로 회전시키면 값을 0에서 7까지 증가시키는데 7에서 8이 되는게 아니라 0이 된다!, 반시계방향으로 회전하면 7에서 0방향으로 감소시키는데 0에서 1감소되면 -1이 되는게 아니라 7이된다!

/* USER CODE BEGIN 2 */
 uint8_t old_A = 0;
 int cnt = 0;
 char buff[50];
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	  //녹칸다가 풀업방식으로 연결해놓음
	  uint8_t sw = HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7);
	  if(sw == 0){
		  HAL_UART_Transmit(&huart2, "SW\n", 3, 100);
		  HAL_Delay(200);
	  }
	  //PA11의 상승엣지가 발생했을때
	  uint8_t now_A = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
	  if(old_A == 0 && now_A == 1){
		  //PA12의 값을 측정해서 LOW면 시계, HIGH면 반시계
		  uint8_t now_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
		  if(now_B){
			 //반시계
			  cnt--;
			  if(cnt < 0) cnt = 7;
			  sprintf(buff,"CNT = %d\n",cnt);
			  HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
		  }else{
			 //시계
			  cnt++;
			  if(cnt > 7) cnt = 0;
			  sprintf(buff,"CNT = %d\n",cnt);
			  HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
		  }
	  }
	  old_A = now_A;
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


6.로터리 엔코더를 회전시킬때, LED가 12시방향에서 출발해서 시계방향으로 로터리엔코더를 돌리면 함께 회전하고, 반시계방향으로 회전시키면 반시계방향으로 1칸씩 이동하시오!

/* USER CODE BEGIN 0 */
GPIO_TypeDef *mygroup[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
void set_led(int num){
	//LED의 상태 업데이트 현재 켜져야하는 LED는 CNT번째꺼
	  for(int i = 0;i<8;i++){
		  if(i == num){
			  HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_SET);
		  }else{
			  HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_RESET);
		  }
	  }
}
/* USER CODE END 0 */

//중략

/* USER CODE BEGIN 2 */
 uint8_t old_A = 0;
 int cnt = 0;
  //시작하자마자 일단 12시방향의 LED는 1개 켜고 출발한다!
  HAL_GPIO_WritePin(mygroup[0],mypin[0],GPIO_PIN_SET);
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	  //녹칸다가 풀업방식으로 연결해놓음
	  uint8_t sw = HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7);
	  if(sw == 0){
		  HAL_UART_Transmit(&huart2, "SW\n", 3, 100);
		  HAL_Delay(200);
	  }
	  //PA11의 상승엣지가 발생했을때
	  uint8_t now_A = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
	  if(old_A == 0 && now_A == 1){
		  //PA12의 값을 측정해서 LOW면 시계, HIGH면 반시계
		  uint8_t now_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
		  if(now_B){
			 //반시계
			  cnt--;
			  if(cnt < 0) cnt = 7;
			  set_led(cnt);
		  }else{
			 //시계
			  cnt++;
			  if(cnt > 7) cnt = 0;
			  set_led(cnt);
		  }
	  }
	  old_A = now_A;
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


7.이번에는 인터럽트를 이용해서 로터리엔코더의 회전방향을 시리얼로 출력하시오!

/* USER CODE BEGIN 0 */
volatile int direct = 0; //0이면 멈춤, 1이면 시계, -1이면 반시계
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_11)
  {
      //로터리엔코드의 A핀에 상승엣지가 검출됨!
	   //B핀의 상태만 확인하면됨!
	   uint8_t now_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
	   if(now_B){
		   //반시계
		   direct = -1;
	   }else{
		   //시계
		   direct = 1;
	   }
  }
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	  //어라? 로터리엔코더가 시계방향으로 돌아갔네?
	  if(direct == 1){
		  HAL_UART_Transmit(&huart2, "CW\n", 3, 100);
		  direct = 0;
	  }else if(direct == -1){
		  HAL_UART_Transmit(&huart2, "CCW\n", 4, 100);
		  direct = 0;
	  }
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


8.인터럽트 방식으로 예제 6번에서 했던 LED 빙글빙글 회전시키는 예제를 구현하시오!

/* USER CODE BEGIN 0 */
volatile int direct = 0; //0이면 멈춤, 1이면 시계, -1이면 반시계
uint32_t nockanda_timer = 0;
//HAL_GetTick
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_11)
  {
	   //로터리엔코더 인식부분을 1밀리초 간격으로 실행되도록 하겠다!
	   if(HAL_GetTick() - nockanda_timer > 1){
		   nockanda_timer = HAL_GetTick();
		   //로터리엔코드의 A핀에 상승엣지가 검출됨!
		   //B핀의 상태만 확인하면됨!
		   uint8_t now_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
		   if(now_B){
			   //반시계
			   direct = -1;
		   }else{
			   //시계
			   direct = 1;
		   }
	   }
  }
}
GPIO_TypeDef *mygroup[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
void set_led(int num){
	//LED의 상태 업데이트 현재 켜져야하는 LED는 CNT번째꺼
	  for(int i = 0;i<8;i++){
		  if(i == num){
			  HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_SET);
		  }else{
			  HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_RESET);
		  }
	  }
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
 int cnt = 0;
 HAL_GPIO_WritePin(mygroup[0],mypin[0],GPIO_PIN_SET);
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	  //어라? 로터리엔코더가 시계방향으로 돌아갔네?
	  if(direct == 1){
		  cnt++;
		  if(cnt > 7) cnt = 0;
		  set_led(cnt);
		  direct = 0;
	  }else if(direct == -1){
		  cnt--;
		  if(cnt < 0) cnt = 7;
		  set_led(cnt);
		  direct = 0;
	  }
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
반응형
Posted by 덕력킹
,