[STM32#19] STM32에서 형식이 있는 데이터를 sscanf로 수신하는 방법과 ring buffer 활용방법 알아보기!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2025. 12. 15. 23:19
https://youtube.com/live/sM4WSj0hdWk
[STM32#19] STM32에서 형식이 있는 데이터를 sscanf로 수신하는 방법과 ring buffer 활용방법 알아보기!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g3b065dcc8fb_1_0#slide=id.g3b065dcc8fb_1_0
STM32에서 sscanf와 ring buffer를 활용해봅시당!
1.C#에서 아래와 같은 데이터를 전송할때 STM32가 해석해서 응답을 보낼 수 있도록 sscanf로 기능 구현을 하시오!
(데이터) “num1=1234\n” 일때 STM32는 1234를 추출한다!
(응답) “num1 is 1234!!\n”

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int num1 = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 num1=1234
if(sscanf(rx_buff,"num1=%d",&num1) == 1){
//성공
sprintf(tx_buff,"num1 is %d!!\n",num1);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else{
//실패
HAL_UART_Transmit(&huart2, "error!\n", 7, 100);
}
rx_pos = 0;
}
//수신인터럽트 다시 시작
HAL_UART_Receive_IT(&huart2, &data, 1);
}
private void button1_Click(object sender, EventArgs e)
{
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200; //usart2
serialPort1.Open(); //개방!
if (serialPort1.IsOpen)
{
serialPort1.ReadExisting();
MessageBox.Show("연결완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string data = serialPort1.ReadLine();
richTextBox1.Text += "[수신]=" + data + "\n";
}
private void button2_Click(object sender, EventArgs e)
{
//포트가 열려있고, 입력창이 빈칸이 아닐때~
if (serialPort1.IsOpen && textBox2.Text != "")
{
serialPort1.WriteLine(textBox2.Text);
}
}
2.이번에는 아래와같은 커맨드 형식일때 여러개의 명령어를 인식가능하도록 하시오!
(데이터1) “select value = 10”
(데이터2) “update value = 10”
(데이터3) “delete value = 10”
(응답) “cmd is select, value is 10”

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int num1 = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 “select value = 10”
if(sscanf(rx_buff,"select value = %d",&num1) == 1){
//성공
sprintf(tx_buff,"cmd is select, value is %d\n",num1);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else if(sscanf(rx_buff,"update value = %d",&num1) == 1){
//성공
sprintf(tx_buff,"cmd is update, value is %d\n",num1);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else if(sscanf(rx_buff,"delete value = %d",&num1) == 1){
//성공
sprintf(tx_buff,"cmd is delete, value is %d\n",num1);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else{
//실패
HAL_UART_Transmit(&huart2, "error!\n", 7, 100);
}
rx_pos = 0;
}
//수신인터럽트 다시 시작
HAL_UART_Receive_IT(&huart2, &data, 1);
}
(C#코드는 예제1과 같음)
3.예제2에서 녹칸다가 제기한 원인을 해결하는 방법에서 약간 다른 방법을 제시해보시오!(여러개의 조건문을 1개로 압축하기)

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int num1 = 0;
char cmd[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 “select value = 10”
if(sscanf(rx_buff,"%s value = %d",cmd,&num1) == 2){
//성공
sprintf(tx_buff,"cmd is %s, value is %d\n",cmd,num1);
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else{
//실패
HAL_UART_Transmit(&huart2, "error!\n", 7, 100);
}
rx_pos = 0;
}
//수신인터럽트 다시 시작
HAL_UART_Receive_IT(&huart2, &data, 1);
}
(C#코드는 예제1과 같음)
4.녹칸다가 아래와 같은 명령어를 입력하면 STM32의 PB3 LED가 제어된다!
(LED OFF) “LED=0” (LED ON) “LED=1”

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int state = 0;
char cmd[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 "LED=0"
if(sscanf(rx_buff,"LED=%d",&state) == 1){
//성공
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, state);
HAL_UART_Transmit(&huart2, "LED IS OFF!\n", 12, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, state);
HAL_UART_Transmit(&huart2, "LED IS ON!\n", 11, 100);
}
}else{
//실패
HAL_UART_Transmit(&huart2, "error!\n", 7, 100);
}
rx_pos = 0;
}
//수신인터럽트 다시 시작
HAL_UART_Receive_IT(&huart2, &data, 1);
}
(C#코드는 예제1과 같음)
5.이번에는 예제4와 동일한 구성을 가지면서 STM32쉴드에 붙어있는 LED 8개를 개별적으로 제어할 수 있는 명령어 체계를 구성하시오!
“LED@=%” : LED번호는 @, ON/OFF여부는 % (예시) “LED2=1” : LED2번을 ON해라!

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int led_num = 0;
int state = 0;
char cmd[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 "LED@=%"
if(sscanf(rx_buff,"LED%d=%d",&led_num,&state) == 2){
//성공
if(led_num == 1){
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, state);
HAL_UART_Transmit(&huart2, "LED1 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, state);
HAL_UART_Transmit(&huart2, "LED1 IS ON!\n", 12, 100);
}
}else if(led_num == 2){
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, state);
HAL_UART_Transmit(&huart2, "LED2 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, state);
HAL_UART_Transmit(&huart2, "LED2 IS ON!\n", 12, 100);
}
}else if(led_num == 3){
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, state);
HAL_UART_Transmit(&huart2, "LED3 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, state);
HAL_UART_Transmit(&huart2, "LED3 IS ON!\n", 12, 100);
}
}else if(led_num == 4){
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, state);
HAL_UART_Transmit(&huart2, "LED4 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, state);
HAL_UART_Transmit(&huart2, "LED4 IS ON!\n", 12, 100);
}
}else if(led_num == 5){
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, state);
HAL_UART_Transmit(&huart2, "LED5 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, state);
HAL_UART_Transmit(&huart2, "LED5 IS ON!\n", 12, 100);
}
}else if(led_num == 6){
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, state);
HAL_UART_Transmit(&huart2, "LED6 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, state);
HAL_UART_Transmit(&huart2, "LED6 IS ON!\n", 12, 100);
}
}else if(led_num == 7){
if(state == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, state);
HAL_UART_Transmit(&huart2, "LED7 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, state);
HAL_UART_Transmit(&huart2, "LED7 IS ON!\n", 12, 100);
}
}else if(led_num == 8){
if(state == 0){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, state);
HAL_UART_Transmit(&huart2, "LED8 IS OFF!\n", 13, 100);
}else if(state == 1){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, state);
HAL_UART_Transmit(&huart2, "LED8 IS ON!\n", 12, 100);
}
}
}else{
//실패
HAL_UART_Transmit(&huart2, "error!\n", 7, 100);
}
rx_pos = 0;
}
//수신인터럽트 다시 시작
HAL_UART_Receive_IT(&huart2, &data, 1);
}
(C#코드는 예제1과 같음)
6.예제5번에서 코드를 좀 압축해보시오!

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int led_num = 0;
int state = 0;
char cmd[20];
GPIO_TypeDef *mygpio[] = {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 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 "LED@=%"
if(sscanf(rx_buff,"LED%d=%d",&led_num,&state) == 2){
//성공
if(led_num >=0 && led_num<=8 && (state == 0 || state == 1)){
HAL_GPIO_WritePin(mygpio[led_num-1], mypin[led_num-1], state);
sprintf(tx_buff,"LED%d IS %s\n",led_num,state == 0 ? "OFF" : "ON");
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else{
HAL_UART_Transmit(&huart2, "error-2!\n", 9, 100);
}
}else{
//실패
HAL_UART_Transmit(&huart2, "error-1!\n", 9, 100);
}
rx_pos = 0;
}
//수신인터럽트 다시 시작
HAL_UART_Receive_IT(&huart2, &data, 1);
}
(C#코드는 예제1과 같음)
7.예제6번에서 동작하는 방식을 JSON명령어를 인식하도록 하시오!
{"led_num":1,"state":0} : LED1 OFF
{"led_num":3,"state":1} : LED3 ON

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int led_num = 0;
int state = 0;
char cmd[20];
GPIO_TypeDef *mygpio[] = {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 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 "{"led_num":1,"state":0}"
if(sscanf(rx_buff,"{\"led_num\":%d,\"state\":%d}",&led_num,&state) == 2){
//성공
if(led_num >=0 && led_num<=8 && (state == 0 || state == 1)){
HAL_GPIO_WritePin(mygpio[led_num-1], mypin[led_num-1], state);
sprintf(tx_buff,"LED%d IS %s\n",led_num,state == 0 ? "OFF" : "ON");
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else{
HAL_UART_Transmit(&huart2, "error-2!\n", 9, 100);
}
}else{
//실패
HAL_UART_Transmit(&huart2, "error-1!\n", 9, 100);
}
rx_pos = 0;
}
HAL_UART_Receive_IT(&huart2, &data, 1);//수신인터럽트 다시 시작
}
(C#코드는 예제1과 같음)
8.데이터 형식이 JSON이 되었으니, C#에서도 JSON데이터를 생성할 수 있게 되었다! C#에 JSON라이브러리를 추가해서 조금더 고급스러운 기능을 보이시오!

/* USER CODE BEGIN 0 */
char rx_buff[100];
char tx_buff[100];
int rx_pos = 0;
char data;
int led_num = 0;
int state = 0;
char cmd[20];
GPIO_TypeDef *mygpio[] = {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 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//데이터 처리(종료문자가 \n인 상황)
rx_buff[rx_pos++] = data;
if(data == '\n'){
//STM32가 echo
rx_buff[rx_pos] = '\0';
//rx_buff에 있는 데이터 형식 "{"led_num":1,"state":0}"
if(sscanf(rx_buff,"{\"led_num\":%d,\"state\":%d}",&led_num,&state) == 2){
//성공
if(led_num >=0 && led_num<=8 && (state == 0 || state == 1)){
HAL_GPIO_WritePin(mygpio[led_num-1], mypin[led_num-1], state);
sprintf(tx_buff,"LED%d IS %s\n",led_num,state == 0 ? "OFF" : "ON");
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}else{
HAL_UART_Transmit(&huart2, "error-2!\n", 9, 100);
}
}else{
//실패
HAL_UART_Transmit(&huart2, "error-1!\n", 9, 100);
}
rx_pos = 0;
}
HAL_UART_Receive_IT(&huart2, &data, 1);//수신인터럽트 다시 시작
}
using Newtonsoft.Json;
////////////////////////////////////
//C#의 전역위치
class Nockanda
{
public int led_num;
public int state;
}
Nockanda data = new Nockanda();
/////////////////////////////////////
private void button1_Click(object sender, EventArgs e)
{
serialPort1.PortName = textBox1.Text;
serialPort1.BaudRate = 115200; //usart2
serialPort1.Open(); //개방!
if (serialPort1.IsOpen)
{
serialPort1.ReadExisting();
MessageBox.Show("연결완료!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string data = serialPort1.ReadLine();
richTextBox1.Text += "[수신]=" + data + "\n";
}
void send_data(int led_num, int state)
{
if (serialPort1.IsOpen)
{
data.led_num = led_num;
data.state = state;
string output = JsonConvert.SerializeObject(data);
serialPort1.WriteLine(output);
}
}
private void button2_Click(object sender, EventArgs e)
{
send_data(1, 1);
}
private void button3_Click(object sender, EventArgs e)
{
send_data(1, 0);
}
private void button4_Click(object sender, EventArgs e)
{
send_data(2, 1);
}
private void button5_Click(object sender, EventArgs e)
{
send_data(2, 0);
}
private void button6_Click(object sender, EventArgs e)
{
send_data(3, 1);
}
private void button7_Click(object sender, EventArgs e)
{
send_data(3, 0);
}
private void button8_Click(object sender, EventArgs e)
{
send_data(4, 1);
}
private void button9_Click(object sender, EventArgs e)
{
send_data(4, 0);
}
private void button10_Click(object sender, EventArgs e)
{
send_data(5, 1);
}
private void button11_Click(object sender, EventArgs e)
{
send_data(5, 0);
}
private void button12_Click(object sender, EventArgs e)
{
send_data(6, 1);
}
private void button13_Click(object sender, EventArgs e)
{
send_data(6, 0);
}
private void button14_Click(object sender, EventArgs e)
{
send_data(7, 1);
}
private void button15_Click(object sender, EventArgs e)
{
send_data(7, 0);
}
private void button16_Click(object sender, EventArgs e)
{
send_data(8, 1);
}
private void button17_Click(object sender, EventArgs e)
{
send_data(8, 0);
}
9.STM32의 수신인터럽트에서 1개의 문자를 받으면 즉시 응답하도록 해서 putty에서 데이터를 전송하시오!

/* USER CODE BEGIN 0 */
#define b_size 256
uint8_t ring_buffer[b_size];
int head = 0;
int tail = 0;
char data;
char tx_buff[256];
int tx_pos = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//수신인터럽트로 받은 데이터를 ring_buffer에 대입한다
//버퍼에 집어넣는 쪽
ring_buffer[head++] = data;
if(head == b_size) head = 0;
//bad case
//HAL_UART_Transmit(&huart2, &data, 1, 100);
//수신인터럽트 다시 시작
HAL_UART_Receive_IT(&huart2, &data, 1);
}
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, &data, 1); //수신인터럽트 시작
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//버퍼에서 읽는 쪽
if(head != tail){
tx_buff[tx_pos++] = ring_buffer[tail];
if(ring_buffer[tail] == '\n'){
//녹칸다가 보낸 데이터의 끝지점이구나!
tx_buff[tx_pos] = '\0';
tx_pos = 0;
HAL_UART_Transmit(&huart2, tx_buff, strlen(tx_buff), 100);
}
tail++;
if (tail == b_size) tail = 0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}

