수정 (2021.03.30)
LL_DMA_EnableStream 함수가 다른 MCU에서는 LL_DMA_EnableChannel 이라는 다른 이름으로 사용되는 것을 확인하고 PRINTF_ENABLE_STREAM_CHANNEL 이라는 함수에 넣었다.
MCU마다 변경할때 PRINTF_ENABLE_STREAM_CHANNEL의 내용만 변경하면 된다.
마찬가지로 PRINTF_DISABLE_STREAM_CHANNEL의 내용도 변경하면 된다.
PRINTF_CLEAR_FLAG의 함수 또한 MCU마다 FLAG가 다른것을 확인하였고 내부 코드만 MCU에 맞게 변경하면 된다.
수정 (2021.03.21)
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
64
65
66
67
68
69
70
71
72
|
void PRINTF_INIT(USART_TypeDef* UART, DMA_TypeDef* DMA, uint32_t DMA_CHANNEL_STREAM){
circular_buffer.UART = UART;
circular_buffer.DMA = DMA;
circular_buffer.DMA_CHANNEL_STREAM = DMA_CHANNEL_STREAM;
LL_DMA_SetPeriphAddress(DMA,DMA_CHANNEL_STREAM,(uint32_t)&(UART->DR));
LL_USART_EnableIT_TC(UART);
LL_USART_EnableDMAReq_TX(UART);
}
void PRINTF_UART_IRQ(){
if(LL_USART_IsActiveFlag_TC(circular_buffer.UART)){
LL_USART_ClearFlag_TC(circular_buffer.UART);
LL_DMA_DisableStream(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM);
if(circular_buffer.is_full == CIRCULAR_FULL){
LL_DMA_ClearFlag_TE7(circular_buffer.DMA);
LL_DMA_ClearFlag_HT7(circular_buffer.DMA);
LL_DMA_ClearFlag_TC7(circular_buffer.DMA);
LL_DMA_ClearFlag_DME7(circular_buffer.DMA);
LL_DMA_ClearFlag_FE7(circular_buffer.DMA);
if(circular_buffer.state == CIRCULAR_BUSY){
if(circular_buffer.end_position == CIRCULAR_BUFFER_SIZE-1){
circular_buffer.is_full = CIRCULAR_REMAIN;
circular_buffer.start_position = 0;
circular_buffer.end_position = circular_buffer.temp_end_position;
LL_DMA_SetMemoryAddress(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,circular_buffer.end_position+1);
}
else{
circular_buffer.start_position = circular_buffer.end_position+1;
circular_buffer.end_position = CIRCULAR_BUFFER_SIZE -1;
LL_DMA_SetMemoryAddress(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,CIRCULAR_BUFFER_SIZE - circular_buffer.start_position);
}
}
else{
circular_buffer.is_full = CIRCULAR_REMAIN;
circular_buffer.start_position = 0;
circular_buffer.end_position = circular_buffer.temp_end_position;
LL_DMA_SetMemoryAddress(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,circular_buffer.end_position - circular_buffer.start_position + 1);
}
LL_DMA_EnableStream(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM);
}
else if(circular_buffer.end_position != circular_buffer.temp_end_position){
circular_buffer.start_position = circular_buffer.end_position +1;
circular_buffer.end_position = circular_buffer.temp_end_position;
LL_DMA_ClearFlag_TE7(circular_buffer.DMA);
LL_DMA_ClearFlag_HT7(circular_buffer.DMA);
LL_DMA_ClearFlag_TC7(circular_buffer.DMA);
LL_DMA_ClearFlag_DME7(circular_buffer.DMA);
LL_DMA_ClearFlag_FE7(circular_buffer.DMA);
LL_DMA_SetMemoryAddress(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM,circular_buffer.end_position - circular_buffer.start_position + 1);
LL_DMA_EnableStream(circular_buffer.DMA,circular_buffer.DMA_CHANNEL_STREAM);
}
else{
if(circular_buffer.end_position == CIRCULAR_BUFFER_SIZE-1){
circular_buffer.start_position =0;
circular_buffer.end_position = 0;
}
else{
circular_buffer.start_position = circular_buffer.end_position +1;
circular_buffer.end_position = circular_buffer.start_position;
}
circular_buffer.state = CIRCULAR_READY;
}
}
}
|
print.c파일에서 PIRNTF_INIT(), PRINTF_UART_IRQ() 함수로 간소화 시켰다.
main.c 파일과 stm32f4xx_it.c 파일을 보면 두개를 어디에 두고 사용하는지 볼 수 있다.
==========================================
수정 (2021.03.17)
DMA의 TC 인터럽트를 사용하지 않아도 작동되게 조금 수정을 했다.
============================================ (원본)
MCU의 UART 메시지를 출력하기 위해 printf를 사용하여 데이터를 출력했었다.
위 글에서 stdio.h의 printf를 사용한 글이 있지만 Polling 방식을 사용하여 구현했다.
이를 보완하기 위해 Polling 방식이 아닌 DMA를 사용하여 구현해봤다.
UART 코드 생성 (0)
기존 글에서 생성한 설정이랑 같다.
이번에도 HAL을 사용하지 않고 LL드라이버를 사용하여 구현했다.
코드 - print.c (1)
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
64
65
66
67
68
|
CIRCULAR_BUFFER circular_buffer = {0,};
void PRINTF(char* format, ...){
uint32_t str_length;
uint32_t temp_length;
va_list valist;
va_start(valist,format);
str_length=vsprintf(circular_buffer.temp_buffer,format,valist);
va_end(valist);
if(circular_buffer.state == CIRCULAR_READY){
LL_USART_ClearFlag_TC(USART1);
LL_DMA_DisableStream(DMA2,LL_DMA_STREAM_7);
LL_DMA_ClearFlag_TE7(DMA2);
LL_DMA_ClearFlag_HT7(DMA2);
LL_DMA_ClearFlag_TC7(DMA2);
LL_DMA_ClearFlag_DME7(DMA2);
LL_DMA_ClearFlag_FE7(DMA2);
LL_DMA_SetMemoryAddress(DMA2,LL_DMA_STREAM_7,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
circular_buffer.state = CIRCULAR_BUSY;
if(circular_buffer.start_position + str_length > CIRCULAR_BUFFER_SIZE){
memcpy(&circular_buffer.buffer[circular_buffer.start_position],
circular_buffer.temp_buffer,
CIRCULAR_BUFFER_SIZE - circular_buffer.start_position);
temp_length=str_length - (CIRCULAR_BUFFER_SIZE - circular_buffer.start_position);
memcpy(circular_buffer.buffer,&circular_buffer.temp_buffer[str_length-temp_length],temp_length );
LL_DMA_SetDataLength(DMA2,LL_DMA_STREAM_7,CIRCULAR_BUFFER_SIZE - circular_buffer.start_position);
circular_buffer.end_position = CIRCULAR_BUFFER_SIZE -1;
circular_buffer.temp_end_position = str_length - (CIRCULAR_BUFFER_SIZE - circular_buffer.start_position)-1;
circular_buffer.is_full = CIRCULAR_FULL;
}
else{
circular_buffer.end_position += (str_length-1);
circular_buffer.temp_end_position = circular_buffer.end_position;
memcpy(&circular_buffer.buffer[circular_buffer.start_position], circular_buffer.temp_buffer,str_length);
LL_DMA_SetDataLength(DMA2,LL_DMA_STREAM_7,str_length);
circular_buffer.is_full = CIRCULAR_REMAIN;
}
LL_DMA_EnableStream(DMA2,LL_DMA_STREAM_7);
}
else if(circular_buffer.state == CIRCULAR_BUSY){
if(circular_buffer.temp_end_position+1 + str_length > CIRCULAR_BUFFER_SIZE){
memcpy(&circular_buffer.buffer[circular_buffer.temp_end_position+1],
circular_buffer.temp_buffer,
CIRCULAR_BUFFER_SIZE - (circular_buffer.temp_end_position+1));
temp_length=CIRCULAR_BUFFER_SIZE - (circular_buffer.temp_end_position+1);
memcpy(circular_buffer.buffer,&circular_buffer.temp_buffer[temp_length],str_length-temp_length );
circular_buffer.temp_end_position = str_length - temp_length -1;
circular_buffer.is_full = CIRCULAR_FULL;
}
else{
memcpy(&circular_buffer.buffer[circular_buffer.temp_end_position+1], circular_buffer.temp_buffer,str_length);
circular_buffer.temp_end_position += str_length;
}
}
}
|
PIRINTF 함수이며 일반적인 printf 함수의 사용법이랑 똑같다.
내부 코드는 Circular Buffer와 DMA를 사용하기 위해 작성한 코드이다.
CIRCULAR_BUFFER 라는 구조체를 사용한다.
코드 - stm32f4xx_it.c (2)
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if(LL_USART_IsActiveFlag_RXNE(USART1)){
LL_USART_ClearFlag_RXNE(USART1);
cli_buf[0]=LL_USART_ReceiveData8(USART1);
}
if(LL_USART_IsActiveFlag_IDLE(USART1)){
LL_USART_ClearFlag_IDLE(USART1);
uart_receive_condition=1;
}
if(LL_USART_IsActiveFlag_TC(USART1)){
LL_USART_ClearFlag_TC(USART1);
LL_DMA_DisableStream(DMA2,LL_DMA_STREAM_7);
if(circular_buffer.is_full == CIRCULAR_FULL){
if(circular_buffer.state == CIRCULAR_BUSY){
if(circular_buffer.end_position == CIRCULAR_BUFFER_SIZE-1){
circular_buffer.is_full = CIRCULAR_REMAIN;
circular_buffer.start_position = 0;
circular_buffer.end_position = circular_buffer.temp_end_position;
LL_DMA_SetMemoryAddress(DMA2,LL_DMA_STREAM_7,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(DMA2,LL_DMA_STREAM_7,circular_buffer.end_position+1);
}
else{
circular_buffer.start_position = circular_buffer.end_position+1;
circular_buffer.end_position = CIRCULAR_BUFFER_SIZE -1;
LL_DMA_SetMemoryAddress(DMA2,LL_DMA_STREAM_7,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(DMA2,LL_DMA_STREAM_7,CIRCULAR_BUFFER_SIZE - circular_buffer.start_position);
}
}
else{
circular_buffer.is_full = CIRCULAR_REMAIN;
circular_buffer.start_position = 0;
circular_buffer.end_position = circular_buffer.temp_end_position;
LL_DMA_SetMemoryAddress(DMA2,LL_DMA_STREAM_7,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(DMA2,LL_DMA_STREAM_7,circular_buffer.end_position - circular_buffer.start_position + 1);
}
LL_DMA_EnableStream(DMA2,LL_DMA_STREAM_7);
}
else if(circular_buffer.end_position != circular_buffer.temp_end_position){
circular_buffer.start_position = circular_buffer.end_position +1;
circular_buffer.end_position = circular_buffer.temp_end_position;
LL_DMA_SetMemoryAddress(DMA2,LL_DMA_STREAM_7,(uint32_t)&circular_buffer.buffer[circular_buffer.start_position]);
LL_DMA_SetDataLength(DMA2,LL_DMA_STREAM_7,circular_buffer.end_position - circular_buffer.start_position + 1);
LL_DMA_EnableStream(DMA2,LL_DMA_STREAM_7);
}
else{
if(circular_buffer.end_position == CIRCULAR_BUFFER_SIZE-1){
circular_buffer.start_position =0;
circular_buffer.end_position = 0;
}
else{
circular_buffer.start_position = circular_buffer.end_position +1;
circular_buffer.end_position = circular_buffer.start_position;
}
circular_buffer.state = CIRCULAR_READY;
}
}
/* USER CODE END USART1_IRQn 0 */
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
/**
* @brief This function handles DMA2 stream7 global interrupt.
*/
void DMA2_Stream7_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream7_IRQn 0 */
if(LL_DMA_IsActiveFlag_TC7(DMA2)){
LL_DMA_ClearFlag_TC7(DMA2);
LL_DMA_DisableStream(DMA2,LL_DMA_STREAM_7);
}
/* USER CODE END DMA2_Stream7_IRQn 0 */
/* USER CODE BEGIN DMA2_Stream7_IRQn 1 */
/* USER CODE END DMA2_Stream7_IRQn 1 */
}
|
인터럽트를 사용하여 DMA로 문자로 보내고 있을 때 PRINTF 함수가 다시 실행돼도 문제없이 작동하도록 작성한 코드이다.
또한 DMA Interrupt가 없을 시 작동하지 않을 수 있다.
이에 대한 이유는 아직 찾는 중이다.
STM32F407 MCU를 사용하고 있으므로 DMA가 다를 경우 변경해 줘야 한다.
코드 - main.c (3)
1
2
3
4
5
6
7
|
//////////////////////////////////////////////
LL_DMA_SetPeriphAddress(DMA2,LL_DMA_STREAM_7,(uint32_t)&(USART1->DR));
LL_DMA_EnableIT_TC(DMA2,LL_DMA_STREAM_7);
LL_USART_EnableIT_TC(USART1);
LL_USART_EnableDMAReq_TX(USART1);
//////////////////////////////////////////////
|
main 에서는 기본적인 세팅만 해주면 된다.
결과 - (4)
실제 동작시켜 봤을 때 출력되는 결과이다.
-
어느 정도 완성은 했고 DMA TC 인터럽트가 꼭 필요한 이유를 알아봐야 할 것 같다.
또한 다른 칩에서도 정상 동작하는지도 알아봐야 할 것이다.
실제 적용해봐서 문제가 있는지 다시 확인하는 과정이 필요하다.
PRINTF 외에 CLI 코드도 구현해 보고 싶어 조금씩 찾아보는 중이다.
'임베디드 > STM32' 카테고리의 다른 글
[STM32] CLI - (1) (0) | 2021.05.15 |
---|---|
[STM32] CLI Basic - (0) (2) | 2021.03.20 |
[STM32] STM32cubeMX linux (0) | 2021.02.23 |
[STM32] 간단한 ICM20948 쿼터니언 (0) | 2020.09.18 |
[STM32] LL 드라이버 - MS5611 (0) | 2020.08.28 |