이글의 전부 또는 일부, 사진, 소스프로그램 등은 저작자의 동의 없이는 상업적인 사용을 금지합니다. 또한, 비상업적인 목적이라하더라도 출처를 밝히지 않고 게시하는 것은 금지합니다.


이전 글에서는 수신 인터럽트를 이용하여 UART 통신을 해보았습니다. 이번 글에서는 같은 UART 통신이지만 수신을 DMA(Direct Memory Access)로 처리하는 프로그램을 작성해 보겠습니다.

DMA는 말 그대로 주변기기와 데이터를 주고 받을 때에 MPU를 통하지 않고 메모리로 직접 전잘합니다. 이런 방법으로 통신을 하면 MPU의 부담이 줄어들면서도 통신은 매우 빠르게 처리할 수 있습니다. 주변 기기와 대량의 자료를 주고 받을 때에 유용한 방법입니다.

이번에 진행하는 UART 통신은 DMA의 장점을 충분히 살릴만한 예제는 아닙니다. 왜냐하면 DMA는 대량의 데이터를 전달할 때에 그 진가를 발휘할 수 있는데, 이번의 프로젝트는 이전에 만들었던 수신 인터럽트를 이용한 UART 프로젝트에서와 마찬가지로 USART2와 USART3에서 각각 한 바이트의 데이터를 받아서 전달하기 때문입니다. 어쨌든 DMA를 이용하는 방법을 익혀 본다는 의미로 DMA의 맛만 보기로 합니다. 이전의 프로젝트에서와 마찬가리고 이번 프로젝트에서도 데이터 수신만 DMA로 처리합니다.





1. 프로젝트 만들기



이전의 프로젝트에서와 같은 방법으로 STM32CubeMx에서 프로젝트를 만듭니다. 앞으로는 프로젝트 생성시에 꼭 필요한 화면만 켑처하겠습니다. 다음은 이번프로젝트를 위해서 필요한 기능을 지정한 [Pinout] 화면입니다. RCC, SYS, USART2, USART3를 설정했습니다.




다음은 [Clock Configuration]입니다. 아래의 그림과 클럭은 좀 다르게 설정했습니다. 실제 프로젝트에서는 HSE를 선택했고, APB2 버스가 16MHz로 동작하도록 했습니다. 16MHz 이하의 속도에서는 제대로 동작하지 않는 경우가 종종 발생해서 주정했습니다. AVR에서는 7.xxxMHz에서도 잘 작동시켰는데... HAL 드라이버가 편리하기는 하지만 덩치가 너무 크고, 처리 속도도 떨어지는 등 무거운 것이 사실인 듯합니다.




[Configuration]에서 USART2와 USART3의 인터럽트는 해제된 것을 확인하고, DMA를 지정합니다.


  



USART2의 Rx와 USART3의 Rx가 사용할 수 있는 DMA는 각각 DMA1 Stream 5와 DMA1 Stream 3로 이미 정해져 있습니다.




[Project] 메뉴에서 [Settings...]에서 필요한 사항을 지정하고 [Generate Code]를 실행하여 프로젝트를 만듭니다.



2. 코딩 작업



코딩 작업은 별로 할 것이 없습니다. 이전의 글 STM32로 ESP8266 제어하기 - 수신 인터럽트 사용에서 만들었던 코드를 거의 그대로 쓰면 됩니다. 그 프로젝트에서 만들었던 uart.h 파일을 새로 만든 프로젝트의 Inc 폴더에 복사합니다. uart.c 파일은 Src 폴더에 복사합니다. STM32로 ESP8266 제어하기 - 수신 인터럽트 사용의 main.c 파일에 입력했던 내용을 똑같이 입력합니다. 프로그램에서 딱 한 함수의 이름만 수정하면 됩니다. main.c 파일과 uart.c 파일에서 인터럽트가 걸리도록 설정하는 역할을 했던 함수 HAL_UART_Request_IT() 함수명을 HAL_UART_Request_DMA()로 바꾸어 주기만 하면됩니다. 두 함수는 매개 변수도 같습니다. main.c와 uart.c 내용을 게시하면서 본 글을 마감합니다. 먼저 main.c입니다. main.c의 상단에 #include "uart.h"를 입력해 두었습니다.


 /* USER CODE BEGIN WHILE */
  InitUartQueue(&WifiQueue);
  InitUartQueue(&MonitorQueue);
  if (HAL_UART_Receive_DMA(&hWifi, WifiQueue.Buffer, 1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
  if (HAL_UART_Receive_DMA(&hMonitor, MonitorQueue.Buffer, 1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  while (1)
  {

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
    while (WifiQueue.data > 0) GetDataFromUartQueue(&hWifi);
	while (MonitorQueue.data > 0) GetDataFromUartQueue(&hMonitor);

  }
  /* USER CODE END 3 */




uart.c 파일입니다.

#include "stm32f4xx_hal.h"

#include "uart.h"

UARTQUEUE WifiQueue;
UARTQUEUE MonitorQueue;

void InitUartQueue(pUARTQUEUE pQueue)
{
	pQueue->data = pQueue->head = pQueue->tail = 0;
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	pUARTQUEUE pQueue;
	pQueue = (huart->Instance == USART2 ? &WifiQueue:&MonitorQueue);    //if usart2 WifiQueue else MonitorQueue
	pQueue->head++;                                                     //head++
	if (pQueue->head == QUEUE_BUFFER_LENGTH) pQueue->head = 0;          //if queue end head = 0
	pQueue->data++;                                                     //data++
	if (pQueue->data == QUEUE_BUFFER_LENGTH)                            //if queue is full
		GetDataFromUartQueue(huart);
	HAL_UART_Receive_DMA(huart, pQueue->Buffer + pQueue->head, 1);       //set interrupt again
}

void GetDataFromUartQueue(UART_HandleTypeDef *huart)
{
	UART_HandleTypeDef *dst = (huart->Instance == USART2 ? &hMonitor:&hWifi);
	pUARTQUEUE pQueue = (huart->Instance == USART2 ? &WifiQueue:&MonitorQueue);
	if (HAL_UART_Transmit(dst, pQueue->Buffer + pQueue->tail, 1, 3000) != HAL_OK)
	{
	    _Error_Handler(__FILE__, __LINE__);
	}
	pQueue->tail++;
	if (pQueue->tail == QUEUE_BUFFER_LENGTH) pQueue->tail = 0;
	pQueue->data--;
	HAL_Delay(1);
}


STM32F407UartDMA.zip




다음 글에서부터는 STM32CubeMx가 만들어 준 코드들을 분석함으로써 STM32에 대해 좀 더 알아 보고자 합니다.


블로그 이미지

엠쿠스

Microprocessor(STM32, AVR)로 무엇인가를 만들어 보고자 학습 중입니다.

,