STM32F10x Study Notes 8 (USART implements serial communication DMA method)

Publisher:乐呵的挑Latest update time:2016-05-05 Source: eefocusKeywords:STM32F10x Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
The USART of STM32F10x supports DMA mode and can generate an interrupt after DMA is completed. This is very helpful for application scenarios that need to receive or send large amounts of data.

 

Few ordinary 8-bit or 16-bit microcontrollers include DMA controllers, so many embedded programmers may not be familiar with DMA. Simply put, direct memory access (DMA) is used to provide high-speed data transfer between peripherals and memory or between memory and memory. Since there is no need for CPU intervention, data can be moved quickly through DMA, which saves CPU resources for other operations.

 

The STM32F10x has two DMA controllers with a total of 12 channels (DMA1 has 7 channels and DMA2 has 5 channels), each of which is dedicated to managing memory access requests from one or more peripherals. There is also an arbitrator to coordinate the priority of each DMA request.

 

According to the STM32 reference manual: "The DMA controller and the Cortex™-M3 core share the system data bus and perform direct memory data transfers. When the CPU and DMA access the same target (RAM or peripherals) at the same time, the DMA request will suspend the CPU from accessing the system bus for several cycles, and the bus arbitrator performs a circular schedule to ensure that the CPU can get at least half of the system bus (memory or peripheral) bandwidth." So we don't have to worry about the DMA controller monopolizing bus resources. The CPU can always get general bus time.

 

Next, we will take the data transmission of USART2 as an example to introduce DMA. First, from Figure 22 of the STM32 reference manual, we can see that the transmission function of USART2 can use the 7th channel of DMA1.

 

The setup work of using DMA transfer can be roughly divided into 6 steps:

1. Set the address of the peripheral register in the DMA1_CPAR7 register. When a peripheral data transfer request occurs, this address will be the source or destination of the data transfer. 

2. Set the address of the data memory in the DMA1_CMAR7 register. When a peripheral data transfer request occurs, the transferred data will be read from or written to this address. 

3. Set the amount of data to be transferred in the DMA1_CNDTR7 register. This value is decremented after each data transfer. 

4. Set the priority of the channel in the PL[1:0] bits of the DMA1_CCR7 register. 

5. Set the direction of data transfer, circular mode, incremental mode of peripherals and memory, data width of peripherals and memory, interruption when half of the transfer is generated, or interruption when the transfer is completed in the DMA1_CCR7 register. 

6. Set the ENABLE bit in the DMA1_CCR7 register to enable the channel. 

 

The code corresponding to step 1 is:

 
  1. DMA1_Channel7->CPAR = (uint32_t) &(USART2->DR);  

The code corresponding to step 2 is as follows, where p_str is a pointer pointing to the first address of the data to be transferred:

 

  1. DMA1_Channel7->CMAR = (uint32_t) p_str;  
The code corresponding to step 3 is as follows. cnt is the amount of data to be transmitted. The serial port data is transmitted in bytes, so cnt here is the number of bytes of data to be transmitted.

 

  1. DMA1_Channel7->CNDTR = cnt;  
The code corresponding to step 4 is as follows. The priority of DMA channel is divided into 4 levels: DMA_Priority_VeryHigh, DMA_Priority_High, DMA_Priority_Medium, DMA_Priority_Low. Here it is set to the lowest.

 

  1. DMA1_Channel7->CCR |= DMA_Priority_Low;  
The code corresponding to step 5 is as follows:

 

  1. DMA1_Channel7->CCR |= DMA_DIR_PeripheralDST |   
  2. DMA_Mode_Normal |   
  3. DMA_PeripheralInc_Disable |  
  4. DMA_MemoryInc_Enable |  
  5. DMA_PeripheralDataSize_Byte |  
  6. DMA_MemoryDataSize_Byte |   
  7. DMA_M2M_Disable;  
The code corresponding to step 6 is as follows:

 

  1. DMA1_Channel7->CCR |= DMA_CCR1_EN;  

 

Actually, there should be 2 more steps before these 6 steps. First, before setting up DMA, you need to turn on the DMA clock:

 

  1. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  
Secondly, we need to set up USART to support DMA mode:
 
  1. USARTx->CR3 |= USART_DMAReq_Tx;  

 

Or use the following function:


 

  1. USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);   

 

Once a DMA channel is started, it can respond to DMA requests from peripherals connected to the channel. How to start the next DMA transfer after completing a DMA transfer? This problem has troubled me for several days. Finally, I found the following sentence in the STM32 reference manual:

When the channel is configured in non-circular mode, no DMA operation will be generated after the transfer is completed (that is, the transfer count becomes 0). To start a new DMA transfer, it is necessary to rewrite the transfer count in the DMA_CNDTRx register while the DMA channel is closed.

 

Here is a simple example program:

  1. void USART2_Init(void)  
  2. {  
  3.     GPIO_InitTypeDef GPIO_InitStructure;  
  4.     USART_InitTypeDef USART_InitStructure;  
  5.     NVIC_InitTypeDef NVIC_InitStructure;  
  6.   
  7.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);  
  8.   
  9.     /* Configure USART Tx as alternate function push-pull */  
  10.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  11.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
  12.     GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_50MHz;  
  13.     GPIO_Init(GPIOD, &GPIO_InitStructure);  
  14.   
  15.     /* Configure USART Rx as input floating */  
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  
  18.     GPIO_Init(GPIOD, &GPIO_InitStructure);  
  19.   
  20.     GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);  
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);  
  22.   
  23.     USART_InitStructure.USART_BaudRate = 9600;  
  24.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;  
  25.     USART_InitStructure.USART_StopBits = USART_StopBits_1;  
  26.     USART_InitStructure.USART_Parity = USART_Parity_No;  
  27.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  
  28.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  
  29.     USART_Init(USART2, &USART_InitStructure);  
  30.   
  31.     USART_Cmd(USART2, ENABLE);  
  32. }  
  33.   
  34. void UART2_TX_DMA_Init(uint8_t *p_str, uint16_t cnt)  
  35. {  
  36. // DMA_InitTypeDef DMA_InitStructure;  
  37. // DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(USART2->DR);      
  38. // DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) str;  
  39. // DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  
  40. // DMA_InitStructure.DMA_BufferSize = 14;  
  41. // DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  
  42. // DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  
  43. // DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  
  44. // DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  
  45. // DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  
  46. // DMA_InitStructure.DMA_Priority = DMA_Priority_Low;  
  47. // DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  
  48.   
  49.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  
  50. // DMA_Init(DMA1_Channel7, &DMA_InitStructure);  
  51.   
  52.     DMA1_Channel7->CPAR = (uint32_t) &(USART2->DR);  
  53.     DMA1_Channel7->CMAR = (uint32_t) p_str;  
  54.     DMA1_Channel7->CNDTR = cnt;  
  55.     DMA1_Channel7->CCR = DMA_DIR_PeripheralDST | DMA_Priority_Low |   
  56.                 DMA_Mode_Normal | DMA_PeripheralInc_Disable |  
  57.                 DMA_MemoryInc_Enable | DMA_PeripheralDataSize_Byte |  
  58.                 DMA_MemoryDataSize_Byte | DMA_M2M_Disable;  
  59. }  
  60.   
  61. uint8_t str[] = "Hello World!!!";  
  62. void TaskStart(void *pdata)  
  63. {  
  64.     SysTick_Config(SystemCoreClock/10);  
  65.     USART2_Init();  
  66.     UART2_TX_DMA_Init(str, 14);  
  67.     for(;;)  
  68.     {  
  69.         LED_Spark();  
  70.         //DMA_Cmd(DMA1_Channel7, DISABLE);  
  71.         DMA1_Channel7->CCR &= (uint16_t)(~DMA_CCR1_EN);   
  72.         //DMA_Init(DMA1_Channel7, &DMA_InitStructure);  
  73.         DMA1_Channel7->CNDTR = 14;  
  74.         //DMA_Cmd(DMA1_Channel7, ENABLE);  
  75.         DMA1_Channel7->CCR |= DMA_CCR1_EN;  
  76.         //USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);  
  77.         OSTimeDly(10);  
  78.     }  
  79. }  
  80.   
  81. int main(void)  
  82. {  
  83.     SystemInit();  
  84.     LED_Init();  
  85.     OSInit();  
  86.     OSTaskCreate(TaskStart, (void *)0, &(TaskStartStk[TASK_STK_SIZE-1]), 1);  
  87.     OSStart();  
  88.     for(;;)  
  89.     {  
  90.   
  91.     }      
  92. }  

Next, let's talk about how to generate an interrupt after the DMA transfer is completed. When half of the data is transferred, the half transfer flag (HTIF) is set to 1, and when the half transfer interrupt enable bit (HTIE) is set, an interrupt request will be generated. After the data transfer is completed, the transfer completion flag (TCIF) is set to 1, and when the transfer completion interrupt enable bit (TCIE) is set, an interrupt request will be generated.

There is a TCIE (Transfer completion interrupt enable) bit in the CCR register of DMA  

This bit is set and cleared by software. 

0: Disable TC interrupt 

1: Enable TC interrupt

 

So in order to use DMA interrupts, we need the following code:

 

  1. DMA1_Channel7->CCR |= DMA_IT_TC; //Transfer complete interrupt enable 
  1. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;  
  2. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;  
  3. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  
  4. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
  5. NVIC_Init(&NVIC_InitStructure);  

 

Here is a simple example program. The uCOS semaphore is also used in the example program. The interrupt processing function notifies Task through the semaphore that the DMA transfer is completed:

 

  1. #include "stm32f10x.h"  
  2. #include "uart.h"  
  3. #include "led.h"  
  4. #include "COMMRTOS.H"  
  5. #include "ucos_ii.h"  
  6.   
  7. #define TASK_STK_SIZE 128  
  8. OS_STK TaskStartStk[TASK_STK_SIZE];  
  9.   
  10.   
  11. void USART2_Init(void)  
  12. {  
  13.     GPIO_InitTypeDef GPIO_InitStructure;  
  14.     USART_InitTypeDef USART_InitStructure;  
  15.     NVIC_InitTypeDef NVIC_InitStructure;  
  16.   
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);  
  18.   
  19.     /* Configure USART Tx as alternate function push-pull */  
  20.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  21.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
  22.     GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_50MHz;  
  23.     GPIO_Init(GPIOD, &GPIO_InitStructure);  
  24.   
  25.     /* Configure USART Rx as input floating */  
  26.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
  27.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  
  28.     GPIO_Init(GPIOD, &GPIO_InitStructure);  
  29.   
  30.     GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);  
  31.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);  
  32.   
  33.     USART_InitStructure.USART_BaudRate = 9600;  
  34.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;  
  35.     USART_InitStructure.USART_StopBits = USART_StopBits_1;  
  36.     USART_InitStructure.USART_Parity = USART_Parity_No;  
  37.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  
  38.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  
  39.     USART_Init(USART2, &USART_InitStructure);  
  40.   
  41.     USART_Cmd(USART2, ENABLE);  
  42.   
  43.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;  
  44.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
  45.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  
  46.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
  47.     NVIC_Init(&NVIC_InitStructure);  
  48. }  
  49.   
  50. void UART2_TX_DMA_Init(uint8_t *p_str, uint16_t cnt)  
  51. {  
  52. // DMA_InitTypeDef DMA_InitStructure;  
  53. // DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(USART2->DR);      
  54. // DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) str;  
  55. // DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  
  56. // DMA_InitStructure.DMA_BufferSize = 14;  
  57. // DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  
  58. // DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  
  59. // DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  
  60. // DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  
  61. // DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  
  62. // DMA_InitStructure.DMA_Priority = DMA_Priority_Low;  
  63. // DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  
  64.   
  65.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  
  66. // DMA_Init(DMA1_Channel7, &DMA_InitStructure);  
  67.   
  68.     DMA1_Channel7->CPAR = (uint32_t) &(USART2->DR);  
  69.     DMA1_Channel7->CMAR = (uint32_t) p_str;  
  70.     DMA1_Channel7->CNDTR = cnt;  
  71.     DMA1_Channel7->CCR = DMA_DIR_PeripheralDST | DMA_Priority_Low |   
  72.                 DMA_Mode_Normal | DMA_PeripheralInc_Disable |  
  73.                 DMA_MemoryInc_Enable | DMA_PeripheralDataSize_Byte |  
  74.                 DMA_MemoryDataSize_Byte | DMA_M2M_Disable;  
  75. }  
  76.   
  77. OS_EVENT *UART2_DMA_TX_Sem;  
  78. uint8_t str[] = "Hello World!!!";  
  79. void TaskStart(void *pdata)  
  80. {  
  81.     unsigned char err;  
  82.     SysTick_Config(SystemCoreClock/10);  
  83.       
  84.     USART2_Init();  
  85.     USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);  
  86.     UART2_TX_DMA_Init(str);  
  87.     UART2_DMA_TX_Sem = OSSemCreate(1);      
  88.     for(;;)  
  89.     {  
  90.         LED_Spark();  
  91.         OSSemPend(UART2_DMA_TX_Sem, 0, &err);   
  92.         //DMA_Cmd(DMA1_Channel7, DISABLE);  
  93.         DMA1_Channel7->CCR &= (uint16_t)(~DMA_CCR1_EN);   
  94.         //DMA_Init(DMA1_Channel7, &DMA_InitStructure);  
  95.         DMA1_Channel7->CNDTR = 14;  
  96.         //DMA_Cmd(DMA1_Channel7, ENABLE);  
  97.         DMA1_Channel7->CCR |= DMA_CCR1_EN;  
  98.         //USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);  
  99.         OSTimeDly(10);  
  100.     }  
  101. }  
  102.   
  103. int main(void)  
  104. {  
  105.     SystemInit();  
  106.     LED_Init();  
  107.     OSInit();  
  108.     OSTaskCreate(TaskStart, (void *)0, &(TaskStartStk[TASK_STK_SIZE-1]), 1);  
  109.     OSStart();  
  110.     for(;;)  
  111.     {  
  112.   
  113.     }      
  114. }  
  115.   
  116. void DMA1_Channel7_IRQHandler(void)  
  117. {  
  118.     OS_CPU_SR cpu_sr;  
  119.   
  120.     OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */  
  121.     OSIntNesting++;  
  122.     OS_EXIT_CRITICAL();  
  123.     OSSemPost(UART2_DMA_TX_Sem);  
  124.     //UART_PutChar(USART2, '+');  
  125.     DMA1->IFCR = DMA1_FLAG_TC7;          
  126.     OSIntExit();  
  127. }  
  128.  

Keywords:STM32F10x Reference address:STM32F10x Study Notes 8 (USART implements serial communication DMA method)

Previous article:STM32F10x Study Notes 9 (Solving the problem of JLink being unable to download programs)
Next article:STM32F10x study notes 7 (USART realizes serial communication 3)

Recommended ReadingLatest update time:2024-11-15 14:08

STM32 eight-channel AD conversion is successfully debugged with DMA transfer, and DMA transfer is not misaligned
// // #include "stm32f10x_conf.h"                          //Remove comments from DMA.h and ADC.h #include "stm32f10x.h" #include "stm32_eval.h" #include #define  N  50          //Each channel samples 50 times #define  M  8          //8 channels #define ADC1_DR_Address      ((u32)0x4001244C) vu16  After_
[Microcontroller]
STM32F10x study notes 5 (USART realizes serial communication 1)
The STM32F10x series of microcontrollers all include the USART module, which is a universal synchronous asynchronous receiver and transmitter. The universal synchronous asynchronous receiver and transmitter (USART) provides a flexible method for full-duplex data exchange with external devices using the industrial stand
[Microcontroller]
STM32F10x study notes 5 (USART realizes serial communication 1)
STM32 Notes --- DMA (USART) Demonstration
 Here is a small example to demonstrate that the DMA module works in parallel with the system program.   It takes nearly 10 seconds to send a 10K data at a low baud rate through the serial port. At this time, according to the previous method, the CPU has to keep waiting for data to be sent, sending data; or sending
[Microcontroller]
STM32 DMA application (I) Data transfer between SRAM and flash
1. Why use DMA? DMA stands for Direct Memory Access, which means direct memory access; It can directly operate memory, so it has the following advantages: There is no need for the CPU to access the memory, which frees up the CPU to do other things. Because it can transfer data in memory time without going through the
[Microcontroller]
Record 2--s3c2440 DMA operation
one. typedef struct tagDMA {     volatile U32 DISRC; //0x0 DMA initial source register     volatile U32 DISRCC; //0x4 DMA initial source control register     volatile U32 DIDST; //0x8 DMA initial destination register     volatile U32 DIDSTC; //0xc DMA initial destination control register     volatile U32 DCON; //0x1
[Microcontroller]
USART_IT_TXE and USART_IT_TC of stm32
       Generally speaking, the serial port's sending interrupt to transmit data is not very demanding for my current application, so I have not conducted a good experiment and understanding of it. However, in a serial port program upgrade (IAP) upgrade experiment, I found that someone used this sending interrupt method
[Microcontroller]
Program for using USART of STM32f103 digital power acquisition circuit and connecting with Bluetooth
The STM32 has rich serial port resources and powerful functions. The STM32F103C8T6 used in this project can provide up to 3 serial ports, with a fractional baud rate generator, support for synchronous single-line communication and half-duplex single-line communication, support for LIN, support for modem operation, sma
[Microcontroller]
Program for using USART of STM32f103 digital power acquisition circuit and connecting with Bluetooth
Lesson 7: DMA
DMA is used to connect data transmission between external devices and internal devices. With it, you can bypass the CPU and directly perform data transmission. But at the same time, DMA transmission also uses the data bus. So its use also has advantages and disadvantages. DMA uses a finite state machine for analysis.
[Microcontroller]
Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号