이전 글에 이어서, 프로그램 동작 테스트를 해 보겠습니다.
CUBEMX에 의해 생성된 기본 코드에서, 추가할 내용은 다음과 같습니다.
[main.c]
1. UART_IT_IDLE 인터럽트 설정.
2. UART RX DMA 설정.
[stm32f4xx_it.c]
1. USART2_IRQHandler 함수에서, HAL_UART_IRQHandler 를 사용하지 않고(너무 느림),
IDLE 인터럽트만 처리하도록 수정. IDLE 인터럽트 발생시, DMA rx 인터럽트 강제 발생 후, DMA Disable.
2. DMA1_Stream5_IRQHandler 함수에서 HAL_DMA_IRQHandler 함수를 쓰지 않고,
FIFO 에서 데이터를 사용자 UART 버퍼로 copy 해온 후, DMA Enable.
다음은 위의 내용에 대한 상세 설명 입니다.
[main.c]
1. UART_IT_IDLE 인터럽트 설정.
2. UART RX DMA 설정.
[기본 변수, 및 define 설정 내용]
1 2 3 4 5 6 7 | #define UART_BUFFER_SIZE 1024 #define DMA_RX_BUFFER_SIZE 1024 uint8_t UART_Buffer[UART_BUFFER_SIZE]; uint8_t DMA_RX_Buffer[DMA_RX_BUFFER_SIZE]; size_t Write, Read; | cs |
버퍼 Size 는 대충 잡은 것이니 적당히 수정하시기 바랍니다. 많으면 스택하고 메모리가 많이 들어가니까요.
[설정 코드]
1 2 | __HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); HAL_UART_Receive_DMA(&huart2,DMA_RX_Buffer,DMA_RX_BUFFER_SIZE); | cs |
링크 건, 블로그는 정말 복잡한데, HAL 로 하니 2줄이면 끝. (CUBEMX가 알아서 설정해 줘서 그렇습니다)
[stm32f4xx_it.c]
1. USART2_IRQHandler 함수에서, HAL_UART_IRQHandler 를 사용하지 않고(너무 느림),
IDLE 인터럽트만 처리하도록 수정. IDLE 인터럽트 발생시, DMA rx 인터럽트 강제 발생 후, DMA Disable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /** * @brief This function handles USART2 global interrupt. */ void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ /* Check for IDLE flag */ if (USART2->SR & UART_FLAG_IDLE) { /* We want IDLE flag only */ /* This part is important */ /* Clear IDLE flag by reading status register first */ /* And follow by reading data register */ volatile uint32_t tmp; /* Must be volatile to prevent optimizations */ tmp = USART2->SR; /* Read status register */ tmp = USART2->DR; /* Read data register */ (void)tmp; /* Prevent compiler warnings */ __HAL_DMA_DISABLE(&hdma_usart2_rx); //DMA1_Stream5->CR &= ~DMA_SxCR_EN; /* Disabling DMA will force transfer complete interrupt if enabled */ } return; /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ } | cs |
위에서 HAL_UART_IRQHandler() 함수 전에 return; 을 사용해주면,
간단히 이후의 코드 실행을 막을 수 있습니다. ^^
2. DMA1_Stream5_IRQHandler 함수에서 HAL_DMA_IRQHandler 함수를 쓰지 않고,
FIFO 에서 데이터를 사용자 UART 버퍼로 copy 해온 후, DMA Enable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | /** * @brief This function handles DMA1 stream5 global interrupt. */ void DMA1_Stream5_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */ size_t len, tocopy; uint8_t* ptr; /* Check transfer complete flag */ if (DMA1->HISR & DMA_FLAG_TCIF1_5) { DMA1->HIFCR = DMA_FLAG_TCIF1_5; /* Clear transfer complete flag */ /* Calculate number of bytes actually transfered by DMA so far */ /** * Transfer could be completed by 2 events: * - All data actually transfered (NDTR = 0) * - Stream disabled inside USART IDLE line detected interrupt (NDTR != 0) */ len = DMA_RX_BUFFER_SIZE - DMA1_Stream5->NDTR; tocopy = UART_BUFFER_SIZE - Write; /* Get number of bytes we can copy to the end of buffer */ /* Check how many bytes to copy */ if (tocopy > len) { tocopy = len; } /* Write received data for UART main buffer for manipulation later */ ptr = DMA_RX_Buffer; memcpy(&UART_Buffer[Write], ptr, tocopy); /* Copy first part */ /* Correct values for remaining data */ Write += tocopy; len -= tocopy; ptr += tocopy; /* If still data to write for beginning of buffer */ if (len) { memcpy(&UART_Buffer[0], ptr, len); /* Don't care if we override Read pointer now */ Write = len; } /* Prepare DMA for next transfer */ /* Important! DMA stream won't start if all flags are not cleared first */ DMA1->HIFCR = DMA_FLAG_DMEIF1_5 | DMA_FLAG_FEIF1_5 | DMA_FLAG_HTIF1_5 | DMA_FLAG_TCIF1_5 | DMA_FLAG_TEIF1_5; DMA1_Stream5->M0AR = (uint32_t)DMA_RX_Buffer; DMA1_Stream5->NDTR = DMA_RX_BUFFER_SIZE; __HAL_DMA_ENABLE(&hdma_usart2_rx); //DMA1_Stream5->CR |= DMA_SxCR_EN; /* Start DMA transfer */ } return; /* USER CODE END DMA1_Stream5_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart2_rx); /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */ /* USER CODE END DMA1_Stream5_IRQn 1 */ } | cs |
이렇게 하면, STM32 의 UART RX 에서 FIFO 가 없어서, 데이터를 놓칠 걱정은 더 이상 하지 않아도 됩니다. ^^
주의할 점이 하나 있는데, IDLE 인터럽트를 걸리도록 데이터를 PC 나 다른 장치에서 송신해야 한다는 점입니다.
파일을 전송하는 테스트를 진행해 본 결과,
DMA RX Buffer 보다 큰 크기와 설정한 DMA 길이 보다 큰 데이터 스트링을 보낼 경우는 다음과 같이,
송신하는 측에서 딜레이를 약간 줘야 합니다.
그래야, STM32 에서 DMA 데이터를 USER buff에 copy할 수 있습니다.
저는 tera term 에서 line 당 지연을 1ms 줬습니다.
main.c 의 main() 에서 받은 데이터가 있으면, 그대로 term에 뿌리도록 해서,
파일을 전송 해 보니, 잘 수신 되었음을 알 수 있었습니다.
또 하나, 정말 모르겠던 부분이 있습니다.
HAL_UART_Transmit 함수로 사용해도 되긴 하는데, 너무 내부 코드가 많아서 바꿔 봤는데,
바꾼 코드가 아무리 해도 실행이 안되서, 앞에 __wfi() 나 __wfe() 를 넣어 보니 잘 돌아가더라고요.
순전히 , 여러가지 넣어 보면서 허송 세월을 보내 버렸네요.
왜 이렇게 되는지 혹시 아시는분 댓글 부탁합니다.
1 2 3 | __wfi(); // or __wfe(); while (!(USART2->SR & USART_SR_TXE)); /* Wait till finished */ USART2->DR = UART_Buffer[Read++]; | cs |
위와 동일한 코드
1 | HAL_UART_Transmit(&huart2,(uint8_t *)UART_Buffer[Read++],1,1); | cs |
전체 main() 함수 내용.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ __HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); HAL_UART_Receive_DMA(&huart2,DMA_RX_Buffer,DMA_RX_BUFFER_SIZE); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if (Read != Write) { __wfi(); // or __wfe(); //__HAL_UNLOCK(&huart2); //__HAL_LOCK(&huart2); while (!(USART2->SR & USART_SR_TXE)); /* Wait till finished */ USART2->DR = UART_Buffer[Read++]; //HAL_UART_Transmit(&huart2,(uint8_t *)UART_Buffer[Read++],1,1); //while (!(USART2->SR & USART_SR_TC)); /* Wait till finished */ //while (!(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE))){}; if (Read > UART_BUFFER_SIZE) /* Check buffer overflow */ { Read = 0; } } //while(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin) == GPIO_PIN_RESET); //while(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin) == GPIO_PIN_SET); } /* USER CODE END 3 */ } | cs |
keil 5 로 만든 코드와, cubemx ioc 파일을 첨부합니다.
'STM32F4' 카테고리의 다른 글
[STM32F4xx] USB CDC 테스트 (STM32F411) (0) | 2018.09.06 |
---|---|
[STM32F4xx] UART1 DMA Receive Test (s/w test) (0) | 2018.09.06 |
[STM32F4xx] UART DMA Receive Test (H/W 및 CUBEMX 설정) (0) | 2018.09.06 |
[STM32F4xx] I2C DMA 프로그램 (CUBEMX) (1) | 2018.09.06 |
[STM32F4xx] I2C DMA 설정 (CUBEMX) (0) | 2018.09.06 |