1. Introducción a DMA
DMA (Direct Memory Access) es un periférico del microordenador de un solo chip. Su función principal es mover datos, pero no necesita ocupar la CPU. Es decir, la CPU puede hacer otras cosas al transferir datos. Igual que el multihilo. La transmisión de datos admite desde periféricos a memoria o de memoria a memoria, donde la memoria puede ser SRAM o FLASH. El controlador DMA incluye DMA1 y DMA2. Entre ellos, DMA1 tiene 7 canales y DMA2 tiene 5. Los canales aquí se pueden entender como una especie de tubería para la transmisión de datos. Cabe señalar que DMA2 solo existe en microcontroladores de gran capacidad.
Dos, imagen de solicitud de DMA
Imagen de solicitud DMA1 de cada canal Imagen de solicitud DMA2 de
cada canal
其中 ADC3、SDIO 和 TIM8 的 DMA 请求只在大容量产品中存在,这个在具体项目时 要注意。
3. Nueva construcción
1. Abra el software STM32CubeMX y haga clic en "Nuevo proyecto"
2. Elija MCU y paquete
3. Configure los
ajustes de reloj RCC, seleccione HSE (reloj externo de alta velocidad),
seleccione Configuración de reloj para resonador de cristal / cerámica (oscilador de cristal / resonador de cerámica) , configure el reloj del sistema SYSCLK a 72MHz,
modifique el valor de HCLK a 72, y presiona Enter. Modifica automáticamente todas las configuraciones
4. Es
un paso muy importante configurar el modo de depuración ; de lo contrario, la
configuración SYS del depurador no se reconocerá después del primer programa de programación , seleccione Depurar como cable serial
Cuatro, DMA1
4.1 Configurar el puerto serie
En el conjunto de Connectivity
selección USART1
, seleccione la velocidad en baudios de Asynchronous
comunicación asíncrona . La longitud de los datos transmitidos es . Paridad , bit de parada , recepción y transmisión son todos . Habilitar la interrupción de recepción del puerto serie
115200 Bits/s
8 Bit
None
1
使能
4.2 Configurar DMA
Haga clic en DMA Settings
Agregar USART1 TX DMA1 y USART1 RX correspondientes a los canales 4 y 5.
- Prioridad :
cuando ocurren múltiples solicitudes de canal DMA, significa que hay un problema de orden de procesamiento de respuesta secuencial, que también es administrado por el árbitro. El árbitro gestiona las solicitudes de canal DMA en dos etapas. La primera etapa pertenece a la etapa de software y se puede configurar en el registro DMA_CCRx Hay 4 niveles: muy alta, alta, media y baja prioridad. La segunda etapa pertenece a la etapa de hardware. Si dos o más solicitudes de canal DMA tienen la misma prioridad, su prioridad depende del número de canal. Cuanto menor sea el número, mayor será la prioridad. Por ejemplo, el canal 0 es mayor que el canal 1. Entre los productos de alta capacidad y los productos interconectados, el controlador DMA1 tiene una prioridad más alta que el controlador DMA2. - Modo :
Normal
indica una sola transmisión, la transmisión finaliza después de una transmisión.
Circular
Significa transmisión cíclica. Después de que se complete la transmisión, la transmisión se reanudará nuevamente y el ciclo continuo nunca se detendrá. - Incremento de dirección :
Peripheral
indica que la dirección del periférico aumenta automáticamente.
Memory
Indica que se incrementa la dirección de memoria.
Los datos de envío del puerto serie almacenan datos de forma continua en el registro de datos de envío del puerto serie (USARTx_TDR). Entonces, la dirección externa no se incrementa. La memoria interna almacena los datos que se enviarán, por lo que el puntero de dirección debe incrementarse para enviar todos los datos. - Ancho de datos :
Byte
un byte.
Half Word
Media palabra equivale a dos bytes.
Word
Una palabra equivale a cuatro bytes.
El registro de envío de datos del puerto serie solo puede almacenar 8 bits y enviar un byte a la vez, por lo que la longitud de los datos es Byte.
4.3 Generar código
Nombres de elementos de ruta y
selección de entrada de elementos de un entorno de desarrollo de aplicaciones IDE MDK-ARM V5
cada periférico genera un ’.c/.h’
archivo separado ,
no un gancho: Todo el código de inicialización se genera en main.c
verificado: archivo de código de inicialización generado en el periférico asociado. Por ejemplo, el código de inicialización GPIO se genera en gpio.c.
Haga clic en GENERAR CÓDIGO para generar código
4.4 Transmisión de datos USART + DMA
Crea una nueva variable
uint8_t sendBuff[] = "USART test by DMA\r\n";
Agregue el siguiente código al bucle principal en man.c:
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)sendBuff, sizeof(sendBuff));
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
A través del asistente de puerto serie, puede ver que hay una impresión continua de datos en el área de recepción.
Nota: Si la interrupción del puerto serie no está activada, el programa solo puede enviar datos una vez y el programa no puede juzgar si la transferencia DMA se completa y el USART siempre está en estado de ocupado.
4.5 Recepción de datos USART + DMA
Agregue variables globales al encabezado de main.c Buffer
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
uint8_t Buffer[1];
/* USER CODE END PV */
Declare variables globales al principio de stm32f1xx_it.c Buffer
/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */
extern uint8_t Buffer[1];
/* USER CODE END EV */
En main.c, antes del bucle while y después de inicializar el puerto serie, agregue la función de habilitación de interrupción de recepción, de modo que la interrupción se active cuando se reciban los datos por primera vez.
/**
* @brief The application entry point.
* @retval int
*/
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_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart1, (uint8_t *)Buffer, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Al stm32f1xx_it.c
agregar la parte inferior de este documentoHAL_UART_RxCpltCallback()
/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Receive_DMA(&huart1, (uint8_t *)Buffer, 1);
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Buffer, 1);
}
}
/* USER CODE END 1 */
Envíe OK a través del asistente del puerto serie y podrá ver que se recibe O. Esto se debe a que el conjunto de datos recibido es de un carácter. Si desea recibir más caracteres, aumente el búfer.
4.6 Puerto serie IDLE interrupción inactiva + recepción de datos DMA
caracteristicas:
- Puede recibir y generar cualquier cadena de caracteres.
- Cuando el puerto serie no tiene recepción de datos, no se generará. Después de que se borre la bandera IDLE, el disparador debe iniciarse después de recibir los primeros datos. Una vez que los datos recibidos se interrumpen y no se reciben datos, se genera IDLE. Interrumpido.
Agregue las siguientes variables en main.c:
uint8_t recvBuff[BUFFER_SIZE]; //接收数据缓存数组
volatile uint8_t recvLength = 0; //接收一帧数据的长度
volatile uint8_t recvDndFlag = 0; //一帧数据接收完成标志
Agregue las siguientes definiciones de macro y variables en main.h:
#define BUFFER_SIZE 256
extern uint8_t recvBuff[BUFFER_SIZE]; //接收数据缓存
extern volatile uint8_t recvLength; //接收一帧数据的长度
extern volatile uint8_t recvDndFlag; //一帧数据接收完成标志
En main.c, antes del bucle while y después de que se inicialice el puerto serie, agregue las funciones de habilitación de recepción DMA y interrupción inactiva, de modo que la interrupción se active cuando se reciban datos por primera vez.
/**
* @brief The application entry point.
* @retval int
*/
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_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
HAL_UART_Receive_DMA(&huart1, recvBuff, BUFFER_SIZE);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Al stm32f1xx_it.c
modificar la parte inferior de este documentoUSART1_IRQHandler()
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint32_t tmpFlag = 0;
uint32_t temp;
tmpFlag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
if((tmpFlag != RESET))//idle标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
HAL_UART_DMAStop(&huart1); //
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
recvLength = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
recvDndFlag = 1; // 接受完成标志位置1
HAL_UART_Transmit_DMA(&huart1, recvBuff, recvLength);
recvLength = 0;//清除计数
recvDndFlag = 0;//清除接收结束标志位
memset(recvBuff,0,recvLength);
HAL_UART_Receive_DMA(&huart1, recvBuff, BUFFER_SIZE);//重新打开DMA接收,不然只能接收一次数据
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
Envíe datos de longitud variable a través del asistente de puerto serie
Cinco, asuntos que necesitan atención
El código de usuario se agregará USER CODE BEGIN N
y USER CODE END N
entre, de lo contrario, el próximo uso después de que STM32CubeMX vuelva a generar el código, se eliminará.
• Escrito por Leung el 18 de enero de 2021
• Referencia: Tutorial 6 de la serie STM32CubeMX: Acceso directo a memoria (DMA)
"Guía de desarrollo de Embedded-STM32" Parte dos Conceptos básicos: Capítulo 7 DMA (Biblioteca HAL)