반응형

https://youtube.com/live/NtAF5P2SCoM

[STM32#18] HAL_UART_Receive_IT, HAL_UART_RxCpltCallback수신 인터럽트를 이용해서 각종 모터 제어해보기!(녹칸다 내맘대로 STM32)

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

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

녹칸다의 STM32쉴드에 붙어있는 각종 모터를 제어해보는 것입니다!

 

수신인터럽트 ioc 설정


1.STM32의 usart2수신부분을 폴링방식에서 인터럽트 방식으로 변환한 기본 예제를 구현하시오!

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
/* USER CODE END 0 */

/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 char buff2[100];
 /* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
 while (1)
 {
	  //1초간격으로 stm32의 타이머카운터값을 전송한다!
	  sprintf(buff2,"now getTick = %d\n",HAL_GetTick());
	  HAL_UART_Transmit(&huart2, buff2, strlen(buff2), 100);
	  HAL_Delay(1000);
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }


/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\r'){
		buff[pos] = '\n';
		pos++;
		buff[pos] = '\0';
		//echo
		HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
/* USER CODE END 4 */


2.C#윈폼에서 STM32와 시리얼통신으로 연결되고 버튼1을 누르면 0도, 버튼2를 누르면 90도, 버튼3을 누르면 180도로 서보모터의 각도가 회전되도록 하시오!

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
void set_servo(uint8_t degree){
	//degree는 0~180도 범위이고 CCR4는 600부터 2400의 범위이다!
	TIM2->CCR4 = (uint32_t)(600+(2400-600)*degree/180.0);
}
/* USER CODE END 0 */


/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4); //PWM시작~
 char buff2[100];
 /* USER CODE END 2 */

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



/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\n'){
		buff[pos] = '\0';
		//C#에서 보낸 데이터 한덩어리가 여기서 처리된다!
		//"0" ~ "180"
		int degree = atoi(buff);
		set_servo(degree);
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.PortName = textBox1.Text;
            serialPort1.BaudRate = 115200;
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.ReadExisting();
                MessageBox.Show("접속완료!");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("0");
            }
        }

private void button3_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("90");
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("180");
            }
        }


3.이번에는 컨트롤중에 numericupdown이라는 녀석을 이용해서 사용자가 입력한 숫자값을 버튼을 누르면 STM32에 전송해서 각도를 제어하시오!(STM32코드는 예제2와 동일함)

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
void set_servo(uint8_t degree){
	//degree는 0~180도 범위이고 CCR4는 600부터 2400의 범위이다!
	TIM2->CCR4 = (uint32_t)(600+(2400-600)*degree/180.0);
}
/* USER CODE END 0 */


/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4); //PWM시작~
 char buff2[100];
 /* USER CODE END 2 */

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



/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\n'){
		buff[pos] = '\0';
		//C#에서 보낸 데이터 한덩어리가 여기서 처리된다!
		//"0" ~ "180"
		int degree = atoi(buff);
		set_servo(degree);
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.PortName = textBox1.Text;
            serialPort1.BaudRate = 115200;
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.ReadExisting();
                MessageBox.Show("접속완료!");
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            //전송버튼 클릭이벤트!
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine(numericUpDown1.Value.ToString());
            }
        }


4.이번에는 컨트롤중에 hcrollbar라는 녀석을 이용해서 컨트롤을 드래그해서 서보모터의 각도를 회전시키시오!(STM32코드는 예제2와 동일함)

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
void set_servo(uint8_t degree){
	//degree는 0~180도 범위이고 CCR4는 600부터 2400의 범위이다!
	TIM2->CCR4 = (uint32_t)(600+(2400-600)*degree/180.0);
}
/* USER CODE END 0 */


/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4); //PWM시작~
 char buff2[100];
 /* USER CODE END 2 */

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



/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\n'){
		buff[pos] = '\0';
		//C#에서 보낸 데이터 한덩어리가 여기서 처리된다!
		//"0" ~ "180"
		int degree = atoi(buff);
		set_servo(degree);
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.PortName = textBox1.Text;
            serialPort1.BaudRate = 115200;
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.ReadExisting();
                MessageBox.Show("접속완료!");
            }
        }

        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            label2.Text = hScrollBar1.Value.ToString();
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine(hScrollBar1.Value.ToString());
            }
        }


5.예제4에서 STM32보드가 100밀리초 간격으로 현재 서보모터의 설정 각도를 전송하고 C#은 그 값을 화면에 출력하시오!

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
int degree = 0; //현재 서보모터의 각도값
void set_servo(uint8_t degree){
	//degree는 0~180도 범위이고 CCR4는 600부터 2400의 범위이다!
	TIM2->CCR4 = (uint32_t)(600+(2400-600)*degree/180.0);
}
/* USER CODE END 0 */



/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4); //PWM시작~
 char buff2[100];
 /* USER CODE END 2 */

/* USER CODE BEGIN WHILE */
 while (1)
 {
	  //0.1초간격으로 degree값을 C#으로 전송한다!(종료문자 \n)
	  sprintf(buff2,"%d\n",degree);
	  HAL_UART_Transmit(&huart2, buff2, strlen(buff2), 100);
	  HAL_Delay(100);
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\n'){
		buff[pos] = '\0';
		//C#에서 보낸 데이터 한덩어리가 여기서 처리된다!
		//"0" ~ "180"
		int degree = atoi(buff);
		set_servo(degree);
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.PortName = textBox1.Text;
            serialPort1.BaudRate = 115200;
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.ReadExisting();
                MessageBox.Show("접속완료!");
            }
        }

       private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            label2.Text = hScrollBar1.Value.ToString();
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine(hScrollBar1.Value.ToString());
            }
        }

        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string data = serialPort1.ReadLine();
            textBox2.Text = data;
        }


6.이번에는 STM32의 부저를 활용하는데, C#에서 부저에 발생해야하는 주파수값을 직접적으로 전송하고 0을 전송하면 OFF하는걸로 하시오!

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
static void Tone(uint32_t Frequency)
{
  TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
  TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
/* USER CODE END 0 */




/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
 char buff2[100];
 /* USER CODE END 2 */


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

/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\n'){
		buff[pos] = '\0';
		//C#에서 보낸 데이터 한덩어리가 여기서 처리된다!
		//0이면 OFF이고 0이 아니면 재생한다
		int pitch = atoi(buff);
		if(pitch == 0){
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
		}else{
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
			Tone(pitch);
		}
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.PortName = textBox1.Text;
            serialPort1.BaudRate = 115200;
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.ReadExisting();
                MessageBox.Show("접속완료!");
            }
        }

        private void button2_MouseDown(object sender, MouseEventArgs e)
        {
            //도6
            if (serialPort1.IsOpen) serialPort1.WriteLine("1047");
        }

        private void button2_MouseUp(object sender, MouseEventArgs e)
        {
            //도6
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }

        private void button3_MouseDown(object sender, MouseEventArgs e)
        {
            //레6
            if (serialPort1.IsOpen) serialPort1.WriteLine("1175");
        }
        private void button3_MouseUp(object sender, MouseEventArgs e)
        {
            //레6
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }

        private void button4_MouseDown(object sender, MouseEventArgs e)
        {
            //미6
            if (serialPort1.IsOpen) serialPort1.WriteLine("1319");
        }

        private void button4_MouseUp(object sender, MouseEventArgs e)
        {
            //미6
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }

        private void button5_MouseDown(object sender, MouseEventArgs e)
        {
            //파6
            if (serialPort1.IsOpen) serialPort1.WriteLine("1397");
        }

        private void button5_MouseUp(object sender, MouseEventArgs e)
        {
            //파6
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }


        private void button6_MouseDown(object sender, MouseEventArgs e)
        {
            //솔6
            if (serialPort1.IsOpen) serialPort1.WriteLine("1568");
        }

        private void button6_MouseUp(object sender, MouseEventArgs e)
        {
            //솔6
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }

        private void button7_MouseDown(object sender, MouseEventArgs e)
        {
            //라6
            if (serialPort1.IsOpen) serialPort1.WriteLine("1760");
        }

        private void button7_MouseUp(object sender, MouseEventArgs e)
        {
            //라6
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }

        private void button8_MouseDown(object sender, MouseEventArgs e)
        {
            //시6
            if (serialPort1.IsOpen) serialPort1.WriteLine("1976");
        }


        private void button8_MouseUp(object sender, MouseEventArgs e)
        {
            //시6
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }

        private void button9_MouseDown(object sender, MouseEventArgs e)
        {
            //도7
            if (serialPort1.IsOpen) serialPort1.WriteLine("2093");
        }

        private void button9_MouseUp(object sender, MouseEventArgs e)
        {
            //도7
            if (serialPort1.IsOpen) serialPort1.WriteLine("0");
        }


7.h스크롤바를 이용해서 0~2500정도의 범위에서 값을 STM32에 전송해서 해당되는 진동의 음을 재생하도록 하시오!

8.스탭모터(28byj-48)의 위치제어값을 C#윈폼에서 전송해서 C#에서 요구한 각도대로 회전하게 하시오!

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
uint8_t seq[][4]={
		{0,0,0,1}, //1
		{0,0,1,1}, //2
		{0,0,1,0}, //3
		{0,1,1,0}, //4
		{0,1,0,0}, //5
		{1,1,0,0}, //6
		{1,0,0,0}, //7
		{1,0,0,1}  //8
};
GPIO_TypeDef *mygroup[] = {GPIOC,GPIOC,GPIOC,GPIOD};
uint16_t mypin[] = {GPIO_PIN_10,GPIO_PIN_11,GPIO_PIN_12,GPIO_PIN_2};
int now_pos = 0; //스탭모터가 알고있는 자기의 현재 위치
int target_pos = 0; //녹칸다가 설정한 목표위치
int seq_num = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  //타이머 인터럽트 콜백함수
	if(now_pos < target_pos){
		//시계방향으로 회전해야하는 상황
		for(int z = 0; z<4;z++){
		  HAL_GPIO_WritePin(mygroup[z],mypin[z],seq[seq_num][z]);
		}
		seq_num++;
		if(seq_num >= 8) seq_num = 0;
		now_pos++; //시퀀스 1회당 현재위치 1 증가
	}else if(now_pos > target_pos){
		//반시계방향으로 회전해야 하는 상황
		for(int z = 0; z<4;z++){
		  HAL_GPIO_WritePin(mygroup[z],mypin[z],seq[seq_num][z]);
		}
		seq_num--;
		if(seq_num < 0) seq_num = 7;
		now_pos--; //시퀀스 1회당 현재위치 1 감소
	}
}
/* USER CODE END 0 */


/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 HAL_TIM_Base_Start_IT(&htim2); //타이머인터럽트 시작
 char buff2[100];
 /* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
 while (1)
 {
	  //now_pos
	  sprintf(buff2,"%d\n",now_pos);
	  HAL_UART_Transmit(&huart2, buff2, strlen(buff2), 100);
	  HAL_Delay(100);
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\n'){
		buff[pos] = '\0';
		//C#에서 보낸 데이터 한덩어리가 여기서 처리된다!
		target_pos = atoi(buff);
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
/* USER CODE END 4 */
private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.PortName = textBox1.Text;
            serialPort1.BaudRate = 115200;
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.ReadExisting();
                MessageBox.Show("접속완료!");
            }
        }
private void button2_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("0");
            }
        }

private void button3_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("1024");
            }
        }
private void button4_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("-1024");
            }
        }


9.numericupdown을 이용해서 -4096*3 ~ +4096*3의 범위에서 스탭모터의 위치를 결정할 수 있고, 현재 위치값을 STM32가 100밀리초 간격으로 C#에게 전송해서 화면에 나올 수 있도록 하시오!

/* USER CODE BEGIN 0 */
char recv;
char buff[100]; //수신메모리
int pos = 0;
uint8_t seq[][4]={
		{0,0,0,1}, //1
		{0,0,1,1}, //2
		{0,0,1,0}, //3
		{0,1,1,0}, //4
		{0,1,0,0}, //5
		{1,1,0,0}, //6
		{1,0,0,0}, //7
		{1,0,0,1}  //8
};
GPIO_TypeDef *mygroup[] = {GPIOC,GPIOC,GPIOC,GPIOD};
uint16_t mypin[] = {GPIO_PIN_10,GPIO_PIN_11,GPIO_PIN_12,GPIO_PIN_2};
int now_pos = 0; //스탭모터가 알고있는 자기의 현재 위치
int target_pos = 0; //녹칸다가 설정한 목표위치
int seq_num = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  //타이머 인터럽트 콜백함수
	if(now_pos < target_pos){
		//시계방향으로 회전해야하는 상황
		for(int z = 0; z<4;z++){
		  HAL_GPIO_WritePin(mygroup[z],mypin[z],seq[seq_num][z]);
		}
		seq_num++;
		if(seq_num >= 8) seq_num = 0;
		now_pos++; //시퀀스 1회당 현재위치 1 증가
	}else if(now_pos > target_pos){
		//반시계방향으로 회전해야 하는 상황
		for(int z = 0; z<4;z++){
		  HAL_GPIO_WritePin(mygroup[z],mypin[z],seq[seq_num][z]);
		}
		seq_num--;
		if(seq_num < 0) seq_num = 7;
		now_pos--; //시퀀스 1회당 현재위치 1 감소
	}
}
/* USER CODE END 0 */


/* USER CODE BEGIN 2 */
 HAL_UART_Receive_IT(&huart2, &recv, 1); //수신인터럽트 시작
 HAL_TIM_Base_Start_IT(&htim2); //타이머인터럽트 시작
 char buff2[100];
 /* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
 while (1)
 {
	  //now_pos
	  sprintf(buff2,"%d\n",now_pos);
	  HAL_UART_Transmit(&huart2, buff2, strlen(buff2), 100);
	  HAL_Delay(100);
   /* USER CODE END WHILE */
   /* USER CODE BEGIN 3 */
 }
USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
/* USER CODE BEGIN 4 */
//usart2 수신인터럽트
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//recv에 1개 읽은 문자가 들어있는 상황이다!
	buff[pos] = recv;
	pos++;
	if(recv == '\n'){
		buff[pos] = '\0';
		//C#에서 보낸 데이터 한덩어리가 여기서 처리된다!
		target_pos = atoi(buff);
		pos = 0;
	}
   //수신인터럽트 다시 시작
	HAL_UART_Receive_IT(&huart2, &recv, 1);
}
/* USER CODE END 4 */
private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.PortName = textBox1.Text;
            serialPort1.BaudRate = 115200;
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.ReadExisting();
                MessageBox.Show("접속완료!");
            }
        }
private void button2_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine(numericUpDown1.Value.ToString());
            }
        }

        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string data = serialPort1.ReadLine();
            textBox2.Text = data;
        }
반응형
Posted by 덕력킹
,