개발/기술문의

LabVIEW/STM32 등 개발문의 -> jexe0716@gmail.com 으로 연락부탁드립니다.

STM32/HAL library

DMA를 이용한 UART 통신

jexe 2020. 12. 30. 15:01
반응형

 

STM32 에서는 DMA(Direct Memory Access)를 쉽게? 사용 할 수 있다.

 

The DMA allows data transfers to take place in the background, without the intervention of the Cortex-Mx processor. During this operation, the main processor can execute other tasks and it is only interrupted when a whole data block is available for processing.

 

DMA에 관련한 Application note는 아래 주소를 통해 상세하게 확인할 수 있다.

Using the STM32F2, STM32F4 and STM32F7 Series DMA controller

-----------------------------------------------------------------------------------------------------------------------------------

 

이번 예제는 NUCLEO-F401RE 보드(STM32F401RE)를 사용하였으므로 각자 사용하는 MCU에 맞게 변경하면 될 것이다.

PC에서 명령어를 보내면 STM32가 명령어를 받아서 정상적인 명령어인지, 비정상적인 명령어(노이즈)인지 확인하는 동작을 구현하였다.

 

이번 코드에서 재밌는 점은 CommandLengthTable을 만들어 사용하면 다양한 길이의 명령어를 사용할 수 있다.

(물론 명령어 길이는 1개로 고정되어 있는 것이 가장 좋다)

 

STM32F4에서는NDTR 이지만, STM32F0에서는 CNDTR로 약간 달랐던 것으로 기억한다. 이 부분은 Reference 문서를 참조해야 할 것이다.

 

다이어그램:

(1) START_OF_COMMAND(Header) 체크

(2) Command 길이 체크 - 이번 예제는 7 bytes만 가능하도록 함

(3) Checksum

 

-----------------------------------------------------------------------------------------------------------------------------------

<Cube설정>

먼저 USART2를 설정한다.

 

DMA를 사용하기 위해 DMA Settings에서 Add 버튼을 눌러 RX와 TX를 선택한다.

이때 USART2_RX의 DMA Request Settings 창에 Mode를 Circular로 변경한다.

 

-----------------------------------------------------------------------------------------------------------------------------------

큰 틀로는 SystickCallback을 이용하여 시간 제어를 하였고, 10ms마다 CheckCommand()를 수행하였다.

 

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/* Includes ------------------------------------------*/
#include "stm32f4xx_hal.h"
 
/* External variables -----------------------------------------------*/
extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_tx;
extern DMA_HandleTypeDef hdma_usart2_rx;
 
/* Function prototypes ----------------------------------------------*/
static uint8_t CalcChecksum (int16_t length, uint8_t *pData);
int16_t CheckCommand (void);
void UART_Init(void);
 
/* Private variables ------------------------------------------------*/
#define      COMMAND_RECEIVE_DMA_NDTR    DMA1_Stream5->NDTR
 
#define      START_OF_COMMAND            0x3A
#define        START_OF_RESPONSE           0x3E
 
#define        LENGTH_OF_RECEIVE_BUFFER    64
uint8_t        ReceiveBuffer[LENGTH_OF_RECEIVE_BUFFER];
int16_t        PreviousIndex = LENGTH_OF_RECEIVE_BUFFER - 1;
int16_t        NewIndex;
int16_t        CommandStartIndex;
 
#define        LENGTH_OF_COMMAND_BUFFER    16
uint8_t        CommandBuffer[LENGTH_OF_COMMAND_BUFFER];
int16_t        LengthOfCommand;
 
#define        LENGTH_OF_RESPONSE_BUFFER   128
uint8_t        Response[LENGTH_OF_RESPONSE_BUFFER];
 
int16_t        CommandStartFoundFlag = 0;
 
const uint8_t   CommandLengthTable [256= {
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
 
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
 
  0,   7,   0,   0,   0,   0,   0,   0,   //0x81
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
 
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0
};
 
 
uint32_t    SystemTime = 0;
uint32_t    NoSignalResetTime;
 
// ********************************************************************
// ********************************************************************
void USER_Init(void)
{
    UART_Init();
}
 
 
void USER_Loop(void)
{
    //
}
 
 
void HAL_SYSTICK_Callback(void)
{
    // ****************************************************************
    // System Time
    // ****************************************************************
 
    SystemTime++;
 
    /*
    // overflow에 의해 부작용 방지용으로 idle 기간에 적당하게 reset // > 31 days, = 1day
    if((OperatingMode == IDLE)&&(SystemTime > 2678400000)) SystemTime = 86400000;
    */
 
    if(SystemTime < 1000){
        return;
    }
 
    if(SystemTime % 1000 > 500) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
    else                        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
 
 
    // ****************************************************************
    // 1ms 마다 동작시키기 위한 조치
    // ****************************************************************
 
 
    // ****************************************************************
    // 이하 10ms 마다 동작시키기 위한 조치
    // ****************************************************************
    if(SystemTime % 10 == 0){
        //continue
    }
 
    else{
        return;
    }
    // ****************************************************************
    // 이하 10ms 마다
    // ****************************************************************
    CheckCommand();
 
}
 
// ********************************************************************
// ********************************************************************
int16_t CheckCommand (void)
{
    int16_t    NumCommandReceived;
    uint8_t    CommandByte;
    int16_t    index;
    int16_t i;
 
    // ****************************************************************
    // *** Calc NewIndex from DMA registser ndtr(64 ~1) ***************
    // ****************************************************************
    NewIndex = LENGTH_OF_RECEIVE_BUFFER - 1 - COMMAND_RECEIVE_DMA_NDTR;
    if(NewIndex < 0) NewIndex += LENGTH_OF_RECEIVE_BUFFER;
 
    // ****************************************************************
    // *** If no new data received, return ****************************
    // ****************************************************************
 
    if(NewIndex == PreviousIndex) {
        //no signal이 1초 이상 계속이면... start of command를 새로 search 한다.
        if(SystemTime > NoSignalResetTime) {
            CommandStartFoundFlag = 0;
            NoSignalResetTime = SystemTime + 1000;
        }
        return(0);
    }
 
    // ****************************************************************
    // *** If new data received, continue... **************************
    // ****************************************************************
    NoSignalResetTime = SystemTime + 1000;
 
    // ****************************************************************
    // *** If CommandStart not found,
    //         find CommandStart from new received data ***************
    // ****************************************************************
    if(CommandStartFoundFlag == 0) {
        if(NewIndex > PreviousIndex) {
            for(i = PreviousIndex + 1; i <= NewIndex; i++) {
                if(ReceiveBuffer[i] == START_OF_COMMAND) {
                    CommandStartFoundFlag = 1;
                    CommandStartIndex = i;
                    break;
                }
            }
        }
 
        else {
            for(i = PreviousIndex + 1; i <= LENGTH_OF_RECEIVE_BUFFER; i++) {
                if(ReceiveBuffer[i] == START_OF_COMMAND) {
                    CommandStartFoundFlag = 1;
                    CommandStartIndex = i;
                }
            }
            if(CommandStartFoundFlag == 0) {
                for(i = 0; i <= NewIndex; i++) {
                    if(ReceiveBuffer[i] == START_OF_COMMAND) {
                        CommandStartFoundFlag = 1;
                        CommandStartIndex = i;
                        break;
                    }
                }
            }
        }
    }
 
    // ****************************************************************
    // *** If CommandStart not found, return **************************
    // ****************************************************************
    if(CommandStartFoundFlag == 0) {
        PreviousIndex = NewIndex;
        return(0);
    }
 
    // ****************************************************************
    // *** If CommandStart found, continue ****************************
    // ****************************************************************
 
    // calc number of received command byte
    PreviousIndex = CommandStartIndex;
 
    NumCommandReceived = NewIndex - CommandStartIndex + 1;
    if(NumCommandReceived < 0) NumCommandReceived += LENGTH_OF_RECEIVE_BUFFER;
 
    // if second byte received, calc command length
    if( (PreviousIndex == CommandStartIndex) && (NumCommandReceived >= 2) ) {
        // calc second byte index
        index = CommandStartIndex + 1;
        if(index >= LENGTH_OF_RECEIVE_BUFFER) index -= LENGTH_OF_RECEIVE_BUFFER;
 
        CommandByte = ReceiveBuffer[index];
        LengthOfCommand = CommandLengthTable [CommandByte];
 
        // if LengthOfCommand == less than 3 or more 7, no such command error
        if((LengthOfCommand < 3|| (LengthOfCommand > 7)){
            //no such command error message
            PreviousIndex = CommandStartIndex;
            CommandStartFoundFlag = 0;
            return(0);
        }
    }
 
    // ****************************************************************
    // *** If full command not received, return ***********************
    // ****************************************************************
    if(NumCommandReceived < LengthOfCommand) {
        PreviousIndex = NewIndex;
        return(0);
    }
 
    // ****************************************************************
    // *** If full command found, continue ****************************
    // ****************************************************************
 
    // store received command
    for(i = 0; i < LengthOfCommand; i++) {
        index = CommandStartIndex + i;
        if(index >= LENGTH_OF_RECEIVE_BUFFER) index -= LENGTH_OF_RECEIVE_BUFFER;
 
        CommandBuffer[i] = ReceiveBuffer[index];
    }
 
    // prep for next command
    PreviousIndex = index;
    CommandStartFoundFlag = 0;
 
    // ****************************************************************
    // *** If full command found, continue ****************************
    // ****************************************************************
    if(CalcChecksum (LengthOfCommand, CommandBuffer) != 0) {
        PreviousIndex = CommandStartIndex;
        CommandStartFoundFlag = 0;
        return(0);
    }
 
 
 
    return(1);
 
}
 
 
static uint8_t CalcChecksum (int16_t length, uint8_t *pData)
{
    int16_t i;
    uint16_t sum = 0;
    uint16_t total = 0;
 
    for(i = 0; i < length; i++) {
        sum += pData[i];
    }
    total = sum;
    total &= 0xFF;
    total = ~total + 1;
    total = total + sum;
    total = total&0xff;
    return (total);
}
 
 
void UART_Init(void)
{
    // use one sample bit method (not three sample bit method)
    // one sample bit method disables noise detection feature
    __HAL_UART_ONE_BIT_SAMPLE_ENABLE(&huart2);
 
    // ******** UART Receive using DMA ******************************
    HAL_UART_Receive_DMA(&huart2, ReceiveBuffer,64);
    __HAL_DMA_ENABLE(&hdma_usart2_rx);            // activate dma channel
    // ***************************************************************
}
 
 
cs

 

간단한 UART 통신을 위해 파이썬으로 UART 명령어를 전송했다.

 

STM32CubeIDE의 디버깅으로 CommandBuffer에 명령어가 잘 들어온 것을 확인할 수 있다.

반응형

'STM32 > HAL library' 카테고리의 다른 글

I2C 통신을 이용한 DAC 제어  (0) 2021.01.14
Systick / HAL_SYSTICK_Callback() 사용  (0) 2020.10.01