DMA有两个总线:
1、DMA存储器总线:DMA通过该总线来执行存储器数据的传入和传出。
2、DMA外设总线:DMA通过该总线访问AHB外设(AHB主要是针对高效率、高频宽以及快速系统模块所设计的,主要有Flash 存储器、复位和时钟控制、CRC、以太网、SDIO)或者执行存储器件的数据传输。
FIFO(4级32位存储器缓冲区):源数据传输到目标地址前的临时存储区。当设置为FIFO模式的时候,可以通过软件设置阈值,达到阈值即传输;也可以设置直接模式,立即启动对存储器的传输。
STM32F1的DMA外设请求映像图如图所示,DMA1有7个通道,DMA2有5个通道,这些请求通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。
----------------------------------------------------------------------------------
DMAMUX(DMA请求复用器)
首先介绍关于DMA控制器前一级的多路选择器:
1、外设的DMA请求,以H1为例有107个外设请求,剩下8个用于DMA发生器;
2、通道选择:用于选择DMA具体的选择通道,通道0~7是DMA1的通道0~7,通道8~15是DMA2的通道0~7;
3、同步控制:用于同步DMA请求,可以关闭;
4、请求信号:输出给DMA控制器。
-------------------------------------------------------------------------------------
对于上次的结构体,我们有了新的补充:
DMA_HandleTypeDef g_dma_handler.Init.FIFOMode:是否选用FIFO模式(以下三个成员只有在FIFO模式启动的时候才会生效);
DMA_HandleTypeDef g_dma_handler.Init.FIFOThreshold:FIFO模式的阈值;
DMA_HandleTypeDef g_dma_handler.Init.MemBurst:存储器突发传输;
DMA_HandleTypeDef g_dma_handler.Init.PeriphBurst:外设突发传输。
对于函数我们也有所补充:
HAL_DMA_Start_IT():开始DMA传输;
__HAL_LINKDMA():用于连接DMA和外设句柄;
HAL_UART_Transmit_DMA():使能DMA发送,启动传输;
__HAL_DMA_GET_FLAG():查询DMA传输通道的状态;
__HAL_DMA_ENABLE():使能DMA外设;
__HAL_DMA_DISABLE():失能DMA外设;
HAL_UART_DMAStop():停止DMA传输。
-------------------------------------------------------------------------------------
本次实验直接采用完整的HAL库。
接下来编写主函数代码main.c:
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#Include "./BSP/MPU/mpu.h"
#Include "./BSP/LED/led.h"
#Include "./BSP/LCD/lcd.h"
#Include "./BSP/KEY/key.h"
#Include "./BSP/DMA/dma.h"
const uint8_t TEXT_TO_SEND[] = {"DMA test!!!"};
#define SEND_BUF_SIZE (sizeof(TEXT_TO_SEND) + 2) * 200 //发送数据长度是200条相应数据的长度
uint8_t g_sendbuf[SEND_BUF_SIZE];
int main(void){
uint8_t key = 0;
uint16_t i, k;
uint16_t len;
uint8_t mask = 0;
float pro = 0;
sys_cache_enable();
HAL_Init();
sys_stm32_clock_init(RCC_PLL_MUL9);
delay_init(72);
led_init();
mpu_memory_protection();
lcd_init();
key_init();
usart_init(115200);
LED0(0);
dma_init(DMA2_Stream7, DMA_REQUEST_USART1_TX);
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "DMA TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "AUTO@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY0:Start", RED);
len = sizeof(TEXT_TO_SEND);
k = 0;
for(i = 0; i < SEND_BUF_SIZE; i++){
if(k >= len){ //输出换行符
if(mask){
g_sendbuf[i] = 0x0a;
k = 0;
}
else{
g_sendbuf[i] = 0x0d;
mask++;
}
}
else{
mask = 0;
g_sendbuf[i] = TEXT_TO_SEND[k];
k++;
}
}
i = 0;
while(1){
key = key_scan(0);
if(key == KEY0_PRES){
printf("DMA DATA:\n");
lcd_show_string(30, 130, 200, 16, 16, "Start Transmit...", BLUE);
lcd_show_string(30, 150, 200, 16, 16, " %", BLUE);
HAL_USART_Transmit_DMA(&g_uart1_handle, g_sendbuf, SEND_BUF_SIZE);
while(1){
if(__HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TCIF3_7)){ //等待DMA2_Stream7反馈传输状态(数据流3)
__HAL_DMA_CLEAR_FLAG(&g_dma_handle, DMA_FALG_TCIF3_7);
HAL_UART_DMAStop(&g_uart1_handle);
break;
}
pro = __HAL_DMA_GET_COUNTER(&g_dma_handle);
len = SEND_BUF_SIZE;
pro = 1 - (pro / len);
pro *= 100;
lcd_show_num(30, 150, pro, 3, 16, BLUE);
}
lcd_show_num(30, 150, 100, 3, 16, BLUE); //显示100%
lcd_show_string(30, 130, 200, 16, 16, "Transmit Finished!", BLUE);
}
i++;
delay_ms(10);
if(i == 20){
i = 0;
LED0_TOGGLE();
}
}
}
到这里我们的实验代码就编写完了。