보낼 경우는 몇개의 데이터를 보내는지 알아서 딜레이를 주던가, 주기적으로 상태를 체크하면서 동작 시키면 되는데,
받을 경우에는 가변 길이의 프로토콜일 경우 1개의 데이터가 받아질 때마다 인터럽트 처리를 해야 하는데
데이터 전송률이 높을 수록, 또 수신 데이터가 많아질 수록 함수를 자주 호출하므로 다른일을 못하게 되어 효율이 떨어진다.
3. DMA mode
가장 효율이 좋은 방식으로, 인터럽트 처리 함수를 호출할 필요가 없이 메모리에 직접 데이터가 받아지므로,
주기적으로 받은 데이터를 링버퍼에 쌓아서 처리하면 된다.
외국에서 DMA방식을 써서 많은 잇점이 있다고 하는 글을 첨부해 봅니다.
DMA allows the UART to store data in memory WITHOUT interrupting the processor until the buffer is full saving lots of overhead.
In my case, I told PDC(Peripheral DMA controller) to store UART data into an buffer (byte array) and specified the length. When UART via PDC filled the buffer the PDC issued an interrupt.
In PDC ISR:
Give PDC new empty buffer
Restart UART PDC (so can collect data while we do other stuff in isr)
memcpy full buffer into RINGBUFFER
Exit ISR
As swineone recommended above, implement DMA and you'll love life.
저는 DMA는 링버퍼 구현 때문에 시간이 좀 걸릴 것 같아서,
우선은 다른 구현해야할 동작을 먼저 하고 일단은 RX 인터럽트만 동작을 시켜봤습니다.
이전에 Cube툴에서 UART2 인터럽트를 설정해 놓고 코드를 생성했기 때문에,
이전 글에서 첨부한 소스코드에 Rx 인터럽트 처리함수만 추가해서 1바이트 받으면 바로 TX로 뿌려주는 동작만 구현했습니다.
타이머 인터럽트 처리함수처럼 UART인터럽트도 사용자 콜백함수가 있었습니다.
이 함수 역시도 __weak 키워드가 붙어 있고 이것을 사용하려면 사용할 사용자 파일에 복사해서 붙여넣기를 해서
__weak 키워드를 지워주고 함수 안의 내용을 입맛에 맞게 고쳐주면 됩니다.
처음에 할 일은, 설정은 이미 자동으로 코드가 생성되어 완료가 됐고
1. "내가 uart2 포트(huart2)로 인터럽트 방식을 사용해서 특정 변수에(uart2_rx_ch) 1바이트를 받겠다" 라는 명령을 실행시킵니다.
HAL_UART_Receive_IT(&huart2,&uart2_rx_ch,1);
2. 위의 코드가 실행되면 uart2로 1바이트의 데이터가 수신될 때마다 인터럽트가 걸리는데,
이 때마다 위의 설명에서 말씀드린 사용자 콜백함수로 점프해서 해당 코드를 실행하고 복귀합니다.
아까 말씀 드린 stm324xx_hal.c 파일에 있던 __weak 키워드가 붙어있는 사용자함수를 main.c 파일에 붙여 넣고 함수 내부 코드를 수정한 함수는 다음과 같습니다.
코드의 내용은 "받은 데이터를 폴링방식으로 TX로 출력하고, 다시 인터럽트로 1바이트 받겠다" 입니다.
일종의 echo 동작이죠.
DMA 까지 필요없는 분들은 이 함수를 수정해서 링버퍼에 쌓아서 쓰시던가 그냥 입맛에 맞게 적당히 고쳐서 쓰시면 되겠습니다.
PSOC4 로 Timer Interrupt 를 회사에서 만든 보드로 테스트를 해 봤습니다.
역시 PSOC은 쉽네요.^^
UART는 기본으로 넣었고, 나중에 추가할 I2C도 일단은 H/W 콤포넌트로 추가했습니다.
PSOC4는 UART,SPI,I2C 등의 Serial 통신 모듈은 3 중에 1개를 써도, Serial 통신 모듈 총 2개 중 1개씩 감소합니다. 물론 UDB로 더 만들 수 있지만 UDB가 많이 들어갑니다.
저는 UART 1개, I2C 1개 기본 serial 통신 모듈을 2개 다 사용했네요.
PSOC4에서 타이머 인터럽트 사용하는 내용이 카페에 없어서, 자료를 만들어서 놓습니다.
프로그램시 타이머는 기본으로 사용하는 요소지요.
PSOC4는 타이머를 기본으로 4개를 지원합니다. 더 필요하면 UDB를 사용해서 만들어 넣으면 되는데, UDB가 4개 밖에 없어서 꼭 필요할 때를 위해서 아껴서 쓰는 것이 좋습니다.
<프로그램 테스트 계획>
- 타이머 인터럽트에 의해 주기적(100ms)으로 LED를 토글함.
1. 먼저 TopDesign.cysch 파일에 UART,I2C,Timer 콤포넌트를 추가합니다.
안보여서, 콤포넌트 쪽을 자세히 볼 수 있는 그림을 올립니다.
PSOC Creator 에서 컴파일을 해 보면 우측 옆 태에 Resource Meter를 보면 메모리나 콤포넌트, UDB 의 사용량이 나옵니다.
2. 다음은 Timer 의 설정입니다.
10KHz 의 클럭을 넣었으니 1 주기를 100ms로 맞출려면 1000 번 카운트해야 하므로 Timer 콤포넌트의 period 를 1000으로 수정합니다.
3. Interrupt 는 On terminal count 에 체크하는데, 타이머 끝에서 Interrupt가 발생된다는 의미입니다. Run Mode 는 Continuous 로 계속 반복됩니다. (위 그림 보시면 이해 되겠죠?)
4. System 의 Interrupt 콤포넌트를 Timer Interrupt 출력에 붙입니다.
5. 핀을 회로도에 맞게 지정합니다.
제가 사용하는 보드에는 PSOC4와 다음과 같이 연결되어 있습니다.
6. 이제 프로그램을 수정합시다.
먼저 Main() 함수에서 Timer 인터럽트 함수를 지정합니다. 나중에 설정된 주기마다, 이 인터럽트 함수가 실행됩니다. /* Enable the Interrupt component connected to interrupt */
TC_CC_ISR_StartEx(Timer_INT_Handler);