Uart Interrupt로 LED를 On/Off 할 수 있게 되었다. 

https://pilimage.tistory.com/20

 

[C/STM32] 5. LED ON/OFF - Uart Interrupt제어

Uart Polling으로 LED를 On/Off 할 수 있게 되었다. https://pilimage.tistory.com/19 [C/STM32] 4. LED ON/OFF - Uart Polling 제어 LED를 버튼으로도 제어를 할 수 있게 되었다. https://pilimage.tistory.com/1..

pilimage.tistory.com

 

이제 ring buffer를 이용하여 인터럽트를 수신해보자

https://pilimage.tistory.com/21

 

[C] Ring Buffer / 링버퍼 구현

링 버퍼란? 고정된 크기의 큐의 양 끝을 이어 원형 모양처럼 사용하는 버퍼 데이터를 넣었다가 빼는 역할을 할 수 있다 구현은 다음과 같이 하였다. ring_buffer.h #include #include typedef struct{ uint8_t *bu.

pilimage.tistory.com

 

ring_buffer.h를 Core의 Inc 폴더에 ring_buffer.c를 Core의 Src폴더에 넣고 main에 ring_buffer.h를 include 한다.

ring_buffer를 이용하여 데이터를 수신할 rx_buffer와 수신 받은 데이터를 pop하여 저장하는 rx_frame을 만든다. 

(전역 변수로 선언)

그 후 메인에 ring_buf를 초기화 시킨다.

이제 준비는 모두 끝났다. 

HAL_UART_RxCpltCallback 함수를 위와 같이 수정하였다.

PC와 연결된 uart1번으로 데이터가 수신되면 받은 데이터를 ring_buf에 push 한다. 

PC 콘솔에 입력한 데이터를 출력하기 위해서 HAL_UART_Transmit을 하였고,

HAL_UART_Receive_IT를 통해 다음 데이터 수신을 대기한다.

 

그리고 메인 함수의 루프에서 동작하다 use_console함수를 다음과 같이 수정하였다.

메인 루프에서 반복적으로 ring_buffer에 데이터가 있으면 pop하여 rx_frame에 넣는다. 

데이터의 끝을 알리는 \r나 \n이 아니면 rx_frame의 idx를 증가시키며 ring_buffer에서 꺼낸 데이터를 넣는다. 

데이터의 끝을 알리는 \r나 \n을 만나면 데이터에 맞게 LED를 On / Off 시키도록 수정하였다. 

 

이제 LED를 Uart를 이용하여 On/Off 할 수 있게 되었다...

 

단순히 LED를 On/Off 하는 것 말고도 LED를 서서히 밝아지고 서서히 어두워지게 할 수 있을까?

LED Dimming  / LED Dimmer 라고 하는 기능을 구현해보자

 

언제 ? 

다음글에... 

 

반응형

Uart Polling으로 LED를 On/Off 할 수 있게 되었다. 

https://pilimage.tistory.com/19

 

[C/STM32] 4. LED ON/OFF - Uart Polling 제어

LED를 버튼으로도 제어를 할 수 있게 되었다. https://pilimage.tistory.com/17 [C/STM32] 3. LED ON/Off - 버튼 채터링 추가 버튼을 누르고 있으면 LED가 On되고 버튼을 떼면 LED가 Off되는 것은 이제 쉽다. http..

pilimage.tistory.com

 

Polling방식에서는 데이터가 들어올 때까지 대기하는 시간 동안은 버튼을 눌러도 동작하지 않는 경우가 있었다.  (HAL_UART_Receive의 timeout 시간을 길게 하면 확실히 볼 수 있다.)

 

Uart Interrupt를 이용하여 이를 해결해보자 

 

먼저 Uart Interrput 기능을 설정하기 위해 PinOut & Configuration에서 사용하는 Uart를 선택하고 NVIC Settings  메뉴에서 Interrupt를 Enable 시킨 후, Code Generation을 한다. 

 

이제 Uart Interrupt 기능을 사용할 수 있게 되었다. 

 

먼저 메인 함수의 MX_USART1_UART_Init()을 한 후, HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)를 호출한다.(*pData 는 수신받을 변수, size는 수신받을 데이터 크기)HAL_UART_Receive_IT는 수신 인터럽트를 Enable 하고 수신 대기 상태로 만든다.인터럽트가 발생하면 HAL_UART_IRQHandler 가 호출된다. HAL_UART_IRQHandler에서 자동으로 인터럽트를 처리한 후 Callback 함수를 호출한다. 

여러 Callback 함수들이 있지만 수신과 관련된 RxCpltCallback을 사용한다. 

HAL_UART_RxCpltCallback은 수신을 완료했을때 Callback이 되며 __weak함수이므로 해당 weak 함수를 수정하지 말고 직접 따로 구현을 해주면 된다. 

 

먼저 구현한 HAL_UART_RxCpltCallback은 다음과 같다. 

uart1로 인터럽트가 수신이 되면 데이터를 저장하는 data_arr에 수신받은 데이터를 넣는다. 

수신받은 데이터를 HAL_UART_Transmit_IT로 송신하여 콘솔 창에 출력한다.

그 후, HAL_UART_Receive_IT를 호출하여 다시 수신 대기상태로 만든다. 

(HAL_UART_Receive_IT의 경우 1회용이라고 생각하고 한번 수신을 한 후, 계속  수신을 하려면 반복하여 호출해야 한다. )

 

이제 메인 함수의 루프에서 data_arr을 체크하여 LED를 동작시키면 된다. 

기존의 on/off 에서 LED1이면 LED on / LED0 이면 LED off가 되도록하였다.

 

1. RxCpltCallback에서 수신을 받은 후 idx++을 하기에 메인 루프에서는 idx-1을 하여 '\r' 또는 '\n'을 찾는다. 

2. 해당 조건에 들어가면 '\0'을 넣어 문자열의 끝을 알린다. 

3. 문자열을 비교하여 LED를 On/Off 한다. 

 

해당 코드로 기능들은 잘 동작한다. 

 

하지만 뭔가 찝찝하다. 

일단 메인 루프에 버튼 조작까지  주르륵 늘어있어서 가독성이 별로인 것 같다. 

버튼 조작 부분을 함수로 만들어 버리자

push_btn과 push_tick은 저장된 값을 알고 있어야 하므로 포인터 매개변수로 넘겨주자 

콘솔을 입력하는 부분도 함수로 만들자

data_arr 배열의 경우 RxCallback에도 호출되기 때문에 전역 변수로 사용하였다. 

 

메인의 소스가 훨씬 깔끔해진것 같다. 

하지만 그래도 찝찝하다. 

지금의  RxCpltCallback에서 데이터를 처리하는 방식으로는 문제가 발생할 것 같은 느낌적인 느낌이 난다.

 

만약 메인 루프의 use_console을 처리하는 중에 인터럽트가 발생하여 데이터를 수신한다면 idx가 변경되면서 문제가 생길 수 있지 않을까? 

예를 들어 LED0을 수신받고 use_console의 루틴으로 들어갔는데 바로 LED1을 수신 받으면 data_arr[idx]='\0'을 처리할 때 idx가 바뀌어 다음 LED1이 동작이 안되지 않을까?

 

수신 데이터를 처리하는데 있어서 싸늘하다....불안한다...

이를 좀 더 보완하고자 링 버퍼(Ring Buffer)를 이용한 인터럽트를 사용해보자

언제? 

다음글에서 ....

 

반응형

LED를 버튼으로도 제어를 할 수 있게 되었다. 

https://pilimage.tistory.com/17

 

[C/STM32] 3. LED ON/Off - 버튼 채터링 추가

버튼을 누르고 있으면 LED가 On되고 버튼을 떼면 LED가 Off되는 것은 이제 쉽다. https://pilimage.tistory.com/16 [C/STM32] 2. LED ON/OFF - 버튼 제어 추가 1초 간격으로 LED를 On/off를 하는 것은 이제 쉽다...

pilimage.tistory.com

 

버튼을 손으로 누르는 것 말고 PC로 제어를 할 수 있을까? 

당연히 가능하다.

 

Uart 통신을 이용하여 on이 들어오면 LED가 켜지고, off가 들어오면 LED가 꺼지도록 해보겠다. 

 

USB 컨버터가 없어도 STM32F746은 문제 없다. 

우리에겐 전원 겸 디버깅 용인 ST-LINK가 있다. 

ST-LINK에 VCP포트가 있다.

VCP란 Virtual COM Port로 USB를 통해 PC와 시리얼 통신을 가능하게 해준다.

그러므로 ST-LINK의 VCP_TX/RX를 Uart로 이용하면 된다. 

회로도에서 VCP_TX는 PA9 , VCP_RX는 PB7 인 것을 확인하고 해당 번호의 핀을 클릭하여 각각 USART1_TX, USART1_RX로 선택한다. (선택시 해당 핀 노란색으로 변화)

이제 핀 맵 왼쪽의 Connectivity 항목에서 USART1을 선택하고 Mode를 Asynchronous(비동기)로 선택한다. 

파라미터는 기본적으로 Baudrate는 115200 , word length는 8 bits , Paritiy 는 None , Stop Bits는 1로 설정한다.

그리고 Code Generation을 누르면 환경 설정은 끝이난다. 

 

그리고 이제 다음 상황을 생각하여 소스를 작성하였다.

a. on이 입력되면 LED가 켜지고 off가 들어오면 LED가 꺼지도록 함

b.  on / off 만 정확히 입력되어야 LED 제어, 그외의 입력은 무시 

 -> ex) onnnnn, offff, onoff 등은 동작 무시

c.  테스트에 minicom을 사용하여 키보드로 엔터를 입력할 시 개행문자로 \r (CR)로 입력받음

 -> 터미널마다, 운영체제마다 엔터를 눌렀을 때, 개행문자가 \r :  (CR), \n : (LF) , \r\n : (CRLF)로 다르게 입력

d.  minicom을 이용한 터미널에서 입력한 글씨를 출력하고 가독성을 위해 \n을 추가하여 Uart Transmit을 사용

 

1. HAL_UART_Receive 함수를 사용하여 rcv_data라는 변수에 1바이트씩 데이터를 받아 data_arr라는 버퍼에 저장시켰다. 

2. 받은 데이터가 '\r'이면 문자열의 끝을 알려주는 \0을 추가하였다.

3. strcmp를 사용하여 정확히 on , off만 입력되었을 때 기능이 동작하도록 하였다. 

4. 입력받은 데이터에 \n을 붙여 다시 pc로 전송하여 터미널에서 입력한 데이터를 볼 수 있도록 하였다.

5. 문자열 비교가 끝나면 다음 입력을 위해 idx=0으로 하여 버퍼를 재사용할 수 있도록 한다.

 

위의 소스코드대도 빌드하면 on이 입력되면 LED가 켜지고, off가 입력되면 LED가 꺼진다. 

 

원하는 대로 동작을 시켰지만 특정상황에서만 (\r로 개행될 때) 동작하는 것이 좀 찜찜할 수 도 있다. 

그 예로, echo on > /dev/ttyACM0 처럼 에코를 이용하여 on 또는 off를 보내면 \n이 마지막에 보내져서 기능이 동작하지 않는다..

 

그래서 \r이나 \n을 만나면 판단을 하도록 하였다. 

on인지 off인지 비교하는 부분에서 strcmp대신 strncmp를 이용하여 정해진 길이만큼만 비교하여 on인지 off인지 구분하였다. 

strncmp를 사용하여 문자열의 처음부분만 비교를 하다보니 onoff나 ontime 등도 on으로 인식한다는 문제가 있다.

strstr로 문자열을 검색하는 것 보다는 처음부분 비교하는게 좀 더 나을것 같아서 strncmp를 이용하였다. 

 

on/off를 입력하여 LED도 제어를 해보았다. 

이번 과정에서 가장 큰 문제점은 무엇일까?

 

메인의 while문에 HAL_UART_Receive를 사용하였기에 데이터가 들어올 때까지 timeout(위에서는 100ms)의 시간동안 대기한다. 

즉, 계속해서 Uart를 체크하는 Polling방식을 사용하였기 때문에 데이터가 들어올때까지 대기하는 시간동안은 버튼을 눌러도 동작하지 않는다.  HAL_UART_Receive의 timeout 시간을 길게하면 확실히 볼 수 있다.

 

이러한 현상을 막는 방법은 없을까?

당연히 있다. 

 

바로 인터럽트를 이용하는 방법이다. 

인터럽트를 사용한 방법은 다음 글로...

 

반응형

디버깅 혹은 출력을 보기 위해 stm에서 printf가 필요할 때가 엄~~~청 많은 것 같다.

 

Uart가 있다면 HAL_UART_Transmit을 통해 아주 쉽게 printf와 비슷한 효과를 낼 수 있다. 

 

어떻게 ?

바로 그냥 문자열을 HAL_UART_Transmit을 이용하여 전송하면 된다. 

void print_str(char *str) {
	HAL_UART_Transmit(&huart1, str, strlen(str), 500);
}

int main(){
...

print_str("START\r\n")
...
}

 

만약 문자열이 아니라 변수의 값을 출력해보고 싶다면?

itoa 함수를 이용하면 출력할 수 있다. 

void print_val(char *strtemp, int val) {
	static char temp[128];
	char *ptr;
	ptr = temp;
	memset(temp, 0x00, 128);
	strcpy(temp, strtemp);
	ptr += strlen(temp);
	itoa(val, ptr, 10);
	HAL_UART_Transmit(&huart1, temp, strlen(temp), 500);
}

int main(){
...
a=10;
print_val("state : ",a);
...
}

간단한 출력할 때 유용하게 사용하는 중.. 

반응형

+ Recent posts