目录
一、简介
1.DMA简介
2.一些概念
3.工作原理
二、HAL库的配置
1.时钟树的设置
2.DMA配置
2.1 内存到内存(代码对应3.1)
2.2 内存到外设(代码对应3.2)
三、代码编写
一、简介
1.DMA简介
DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发。
2.一些概念
关于DMA的一些概念,个人认为比较基础,必须要知道,欢迎补充。
地址:DMA的传输过程中,必须要确定源地址和目的地址,在后面代码会有明显体现。
传输模式:内存到内存,内存到外设,外设到内存。本次实验主要验证前两个,原理在实验前会有说明,只做简单验证。
数据宽度:字节,半字,字(4字节)。一般数据传输都用一个字节宽度。
DMA工作模式:正常(Normal)/循环(Circular)。个人认为,除非数据的传输的时间是无法预测,比如串口接收,大部分用正常模式(传输一次后自动关闭)即可,下次需要传递数据时再启动DMA传输。
DMA流的优先级别:非常高/高/中等/低。跟DMA中断非常类似,但不要混淆,流的优先级别越高,会优先进行DMA传输。
其余的概念会在需要补充的地方补充。
3.工作原理
这张图将DMA的工作原理描述的非常清楚,不再赘述。补充一点,外设/内存的地址是否自增看实际情况,像第一个实验,内存到内存,两个内存块的大小大于一个数据宽度,显然需要自增;第二个实验中,将内存的数据通过串口发出,内存的数据块需要自增,而串口发送字寄存器是固定的,不需要自增,理解就好。
二、HAL库的配置
1.时钟树的设置
STM32 hal库使用笔记(一)GPIO的使用—流水灯_乱码小伙的博客-CSDN博客
参考这篇文章中的配置即可。
2.DMA配置
2.1 内存到内存(代码对应3.1)
按照如图所示配置,本次实验通过按键来触发DMA传输并通过LED验证,所以选择正常模式。流的优先级随便,只有一个DMA传输。
配置完成后生成代码即可。
2.2 内存到外设(代码对应3.2)
主要记录关于DMA的配置,串口相关参数默认即可。流的级别也随便,与2.1是两个实验,如果在同一个工程中可按照需要配置;模式是正常模式,本次实验通过按键触发DMA传输;外设非自增,寄存器自增模式,原因前边已经解释。
配置完成后,生成代码即可。
三、代码编写
3.1
用户主要完成的工作就是在每次DMA传输的时候开启DMA传输即可。
以下代码均在dam.c中编写
添加两个数组:
uint8_t src_buf[10] = {0x0a, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
uint8_t dest_buf[10] = {0};
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* Configure DMA request hdma_memtomem_dma1_channel1 on DMA1_Channel1 */
hdma_memtomem_dma1_channel1.Instance = DMA1_Channel1;
hdma_memtomem_dma1_channel1.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma1_channel1.Init.PeriphInc = DMA_PINC_ENABLE; // 增量模式
hdma_memtomem_dma1_channel1.Init.MemInc = DMA_MINC_ENABLE;//增量模式,指针自动向下,否则只会传输/收到第一个字节的数据
hdma_memtomem_dma1_channel1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_memtomem_dma1_channel1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_memtomem_dma1_channel1.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma1_channel1.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_memtomem_dma1_channel1) != HAL_OK)
{
Error_Handler();
}
HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t)src_buf, (uint32_t)dest_buf, 0);//开启DMA传输,由于初始化不需要传输数据,所以数据传输个数是0
}
void dma_enable_transmit(uint16_t cndtr)//DMA传输完成后,传输个数会清零,使能后赋值并开启
{
__HAL_DMA_DISABLE(&hdma_memtomem_dma1_channel1);
// DMA1_Channel1->CNDTR = cndtr;
hdma_memtomem_dma1_channel1.Instance->CNDTR = cndtr;
__HAL_DMA_ENABLE(&hdma_memtomem_dma1_channel1);
}
以下在main.c中编写
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_GPIO_Init();
MX_DMA_Init();
/* USER CODE BEGIN 2 */
// memset(dest_buf, 0, 10);
// dma_enable_transmit(10);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
src_buf[0]++;
src_buf[1]++;
memset(dest_buf, 0, 10);//初始化dest_buf
dma_enable_transmit(10);// 由于内存到内存的传输无法循环,所以需要不断重装载传输的个数
while(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0);
if(dest_buf[0]==src_buf[0])
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
}
if(dest_buf[1]==0x03)
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
}
while (__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1)!=HAL_OK)//如果DMA传输完成,获取标志位,返回值是1
{
__HAL_DMA_CLEAR_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1); //清除标志位
}
}
}
}
/* USER CODE END 3 */
}
只做简单验证,按键每次按下,src_buf[0]++,src_buf[1]++,进行验证。
实现现象:
DMA_内存到内存
3.2
定义发送字符串:uint8_t TEXT_TO_SEND[] = {"正点原子 STM32 DMA 串口实验"};
主函数:
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t TEXT_TO_SEND[] = {"正点原子 STM32 DMA 串口实验"};
/* 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_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0)
{
HAL_UART_Transmit_DMA(&huart1, TEXT_TO_SEND, sizeof(TEXT_TO_SEND));//DMA的发送不是循环模式
while ( __HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TC4)!=HAL_OK) /* 等待 DMA1_Channel4 传输完成 */
{
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx, DMA_FLAG_TC4);
}
while(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0);
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
}
}
}
/* USER CODE END 3 */
}
HAL_UART_Transmit_DMA(&huart1, TEXT_TO_SEND, sizeof(TEXT_TO_SEND));已经完成了DMA启动的功能。
实现现象:
关于串口发送和接收中使用DMA的实验过于简单,后期会再测试复杂些的实验,例如接收不定长字符串/发送不定长字符串。文章来源:https://www.toymoban.com/news/detail-764845.html
欢迎大家交流和指正!!!文章来源地址https://www.toymoban.com/news/detail-764845.html
到了这里,关于STM32 hal库使用笔记(四)DMA—内存到内存/内存到外设的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!