[STM32#16] C# winform을 이용해서 STM32f103rb nucleo보드와 usart2로 serial통신하는 방법 계속알아보기!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2025. 12. 3. 16:51
https://youtube.com/live/oTKsk64gdPY
[STM32#16] C# winform을 이용해서 STM32f103rb nucleo보드와 usart2로 serial통신하는 방법 계속알아보기!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g39a36328931_2_238#slide=id.g39a36328931_2_238
이제 C#윈폼으로 응용을 해봅시다!
1.STM32에서 부호가없는 32bit정수 타입의 값 2개를 1초 간격으로 C#으로 전송한다! 이때 데이터 형식은 CSV 형식이다! C#에서 수신한 데이터를 분리해서 출력하시오!
-(결과예시) num1은 12345이고, num2는 56789이고, num1+num2는 ???입니다!

/* USER CODE BEGIN 2 */
uint32_t num1 = 12345;
uint32_t num2 = 67890;
char buff[100];
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//num1, num2를 csv형식으로 buff에 대입한다
sprintf(buff,"%d,%d\n",num1,num2);
//buff안에 있는 값을 널문자(\0)까지의 길이를 반환해서 전송한다
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
//stm32는 반드시 문자열로 전송하고 종료문자 \n붙힌다!
string data = serialPort1.ReadLine();
//STM32가 보낸 데이터는 CSV형식이다!
string[] csv_data = data.Split(',');
//STM32가 보낸 데이터는 2개이기 때문에 배열의 갯수는 2여야한다!
if(csv_data.Length == 2)
{
//csv_data[0] : num1
//csv_data[1] : num2
UInt32 num1 = UInt32.Parse(csv_data[0]);
UInt32 num2 = UInt32.Parse(csv_data[1]);
UInt32 sum = num1 + num2;
//richTextBox1.Text += "num1=" + num1 + ", num2=" + num2 + ", sum=" + sum + "\n";
richTextBox1.Text += $"num1={num1}, num2={num2}, sum={sum}\n";
}
}
2.예제1번과 동일한 구성인데, STM32가 JSON형식으로 데이터를 전송할때 C#윈폼에서 동일하게 parse해서 richtextbox1에 적절하게 출력하시오!

/* USER CODE BEGIN 2 */
uint32_t num1 = 12345;
uint32_t num2 = 67890;
char buff[100];
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//num1, num2를 csv형식으로 buff에 대입한다
sprintf(buff,"{\"num1\":%d, \"num2\":%d}\n",num1,num2);
//buff안에 있는 값을 널문자(\0)까지의 길이를 반환해서 전송한다
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
using Newtonsoft.Json.Linq; //네임스페이스 추가~
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
//stm32는 반드시 문자열로 전송하고 종료문자 \n붙힌다!
string data = serialPort1.ReadLine();
//data안에 JSON형식의 데이터가 있다!
try
{
JObject json_data = JObject.Parse(data);
UInt32 num1 = UInt32.Parse(json_data["num1"].ToString());// : num1
UInt32 num2 = UInt32.Parse(json_data["num2"].ToString());// : num2
UInt32 sum = num1 + num2;
richTextBox1.Text += $"원본={data}\n";
richTextBox1.Text += $"num1={num1}, num2={num2}, sum={sum}\n";
}
catch
{
//만약 json데이터에 문제가 있다면 이쪽으로 떨어짐!
}
}
3.이번에는 STM32에서 2개의 숫자 데이터를 바이너리로 전송하는데, 공용체를 이용해서 전송해보시오!

/* USER CODE BEGIN 2 */
union{
uint32_t input[2]; //8byte
uint8_t output[8]; //8byte
}myunion;
myunion.input[0] = 12345; //num1
myunion.input[1] = 67890; //num2
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//공용체에 들어갈때는 32bit정수2개지만, 나올때는 8byte 배열이란다~
HAL_UART_Transmit(&huart2,myunion.output,sizeof(myunion.output),100);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
//STM32쪽에서 8byte 배열에 날라온다!
byte[] recv = new byte[8];
int cnt = 0;
while (true)
{
recv[cnt] = (byte)serialPort1.ReadByte();
cnt++;
if (cnt == 8) break;
}
//recv배열안에 0~3이 num1, 4~7까지 num2인상황
UInt32 num1 = BitConverter.ToUInt32(recv, 0);
UInt32 num2 = BitConverter.ToUInt32(recv, 4);
UInt32 sum = num1 + num2;
richTextBox1.Text += $"[수신] = ";
for (int i = 0; i < 8; i++)
{
richTextBox1.Text += recv[i] + ", ";
}
richTextBox1.Text += "\n";
richTextBox1.Text += $"num1={num1}, num2={num2}, sum={sum}\n";
}
4.예제3과 동일한 구성인데, 이번에는 구조체를 활용해서 전송하시오!(녹칸다 생각에는 이방법이 제일 좋은 방법인듯)

/* USER CODE BEGIN 2 */
struct{
uint32_t num1;
uint32_t num2;
}mystruct;
mystruct.num1 = 12345;
mystruct.num2 = 67890;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_UART_Transmit(&huart2,(uint8_t*)&mystruct,sizeof(mystruct),100);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
//STM32쪽에서 8byte 배열에 날라온다!
byte[] recv = new byte[8];
int cnt = 0;
while (true)
{
recv[cnt] = (byte)serialPort1.ReadByte();
cnt++;
if (cnt == 8) break;
}
//recv배열안에 0~3이 num1, 4~7까지 num2인상황
UInt32 num1 = BitConverter.ToUInt32(recv, 0);
UInt32 num2 = BitConverter.ToUInt32(recv, 4);
UInt32 sum = num1 + num2;
richTextBox1.Text += $"[수신] = ";
for (int i = 0; i < 8; i++)
{
richTextBox1.Text += recv[i] + ", ";
}
richTextBox1.Text += "\n";
richTextBox1.Text += $"num1={num1}, num2={num2}, sum={sum}\n";
}
5.녹칸다의 STM32쉴드에 붙어있는 로터리엔코더를 시계방향으로 돌리면 C#윈폼 화면에 값이 1씩 증가하도록하고 반시계방향으로 돌리면 1씩감소하고 스위치를 누르면 0으로 초기화 하도록 하시오!(변화되는 값은 C#의 변수값이다)

/* 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, "0", 1, 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, "-", 1, 100);
}else{
//시계
HAL_UART_Transmit(&huart2, "+", 1, 100);
}
}
old_A = now_A;
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
//C#의 전역변수 위치
int cnt = 0;
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
//STM32에서 문자 1개가 수신된다!
//'+' : 시계방향
//'-' : 반시계방향
//'0' : 초기화
char c = (char)serialPort1.ReadChar();
richTextBox1.Text += c + "\n";
if(c == '0')
{
cnt = 0;
textBox2.Text = cnt.ToString();
}else if (c == '+')
{
cnt++;
textBox2.Text = cnt.ToString();
}
else if (c == '-')
{
cnt--;
textBox2.Text = cnt.ToString();
}
}
6.예제5에서 로터리엔코더를 돌려서 바뀌는 값은 STM32메모리에 기억되고, 변화되는 값을 C#으로 전송해서 화면에 출력하시오!(카운터값이 -10~+10사이면 흰색, +11부터는 빨간색, -11이하면 노란색으로 바꾸시오!)

/* USER CODE BEGIN 2 */
int cnt = 0;
uint8_t old_A = 0;
char buff[50];
char recv;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(HAL_UART_Receive(&huart2, &recv, 1, 1) == HAL_OK){
if(recv == '0'){
//C#에서 현재 CNT값을 1회 전송해달라는 명령!
sprintf(buff,"%d\n",cnt);
HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
}
}
uint8_t sw = HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7);
if(sw == 0){
cnt = 0;
sprintf(buff,"%d\n",cnt);
HAL_UART_Transmit(&huart2, buff, strlen(buff), 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--;
}else{
//시계
cnt++;
}
sprintf(buff,"%d\n",cnt);
HAL_UART_Transmit(&huart2, buff, strlen(buff), 100);
}
old_A = now_A;
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
//C#에서 포트를 개방한 직후 STM32에게 CNT값을 물어본다!
//STM32에게 문자 '0'을 전송하면 STM32는 응답한다!
serialPort1.Write("0");
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
string data = serialPort1.ReadLine();
textBox2.Text = data;
int cnt = int.Parse(data);
if(cnt > 10)
{
//빨간색
textBox2.BackColor = Color.Red;
}else if(cnt < -11)
{
//노란색
textBox2.BackColor = Color.Yellow;
}
else
{
//흰색
textBox2.BackColor = Color.White;
}
}
7.예제5에서 로터리엔코더의 스위치를 누르면 0으로 초기화한다라는 조건 대신에, C#윈폼의 listbox를 이용해서 값을 하나씩 추가하는 것을 보이시오!

/* 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, "0", 1, 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, "-", 1, 100);
}else{
//시계
HAL_UART_Transmit(&huart2, "+", 1, 100);
}
}
old_A = now_A;
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
//C#의 전역변수 위치
int cnt = 0;
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
//STM32에서 문자 1개가 수신된다!
//'+' : 시계방향
//'-' : 반시계방향
//'0' : 초기화
char c = (char)serialPort1.ReadChar();
richTextBox1.Text += c + "\n";
if (c == '0')
{
//cnt = 0;
//textBox2.Text = cnt.ToString();
//listbox1에 현재 cnt값을 추가함!
listBox1.Items.Add(cnt);
}
else if (c == '+')
{
cnt++;
textBox2.Text = cnt.ToString();
}
else if (c == '-')
{
cnt--;
textBox2.Text = cnt.ToString();
}
}
private void button2_Click(object sender, EventArgs e)
{
//listbox1에 있는 내용을 다 지운다!
listBox1.Items.Clear();
}
8.예제5에서 누겟페키지관리로가서 agauge2를 다운받아서 로터리엔코더로 게이지값을 변화시키시오!

/* 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, "0", 1, 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, "-", 1, 100);
}else{
//시계
HAL_UART_Transmit(&huart2, "+", 1, 100);
}
}
old_A = now_A;
HAL_Delay(1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
//C#의 전역변수 위치
int cnt = 0;
private void button1_Click(object sender, EventArgs e)
{
//접속하기 버튼 클릭했다 뭐할래?
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200;
//serialport1의 open메서드를 콜한다!
serialPort1.Open();
if (serialPort1.IsOpen)
{
//버퍼의 직전데이터가 밀려서 들어온다!
serialPort1.ReadExisting();
MessageBox.Show("접속완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//stm32가 데이터를 보냈는데 뭐할래?
//STM32에서 문자 1개가 수신된다!
//'+' : 시계방향
//'-' : 반시계방향
//'0' : 초기화
char c = (char)serialPort1.ReadChar();
richTextBox1.Text += c + "\n";
if (c == '0')
{
cnt = 0;
textBox2.Text = cnt.ToString();
aGauge1.Value = cnt;
}
else if (c == '+')
{
cnt+=5;
if (cnt > 400) cnt = 400;
textBox2.Text = cnt.ToString();
aGauge1.Value = cnt;
}
else if (c == '-')
{
cnt-=5;
if (cnt < -100) cnt = -100;
textBox2.Text = cnt.ToString();
aGauge1.Value = cnt;
}
}

