1. G4系列DMA简介
首先介绍一下G4系列的DMA情况。 32的G4系列虽然说是基于M4内核的,但是他的DMA通道映射和大家较为常见的F407有较大差异,先贴上几张参考手册中的图片: 不知道大家看到这两张图片后能想到什么,相信有看过H7和F4的DMA的朋友一定能反应过来,不错,STM32G4系列基于M4的内核,并且具有和H7一样的DMAMUX(DMA请求复用器)。不熟悉的朋友也没关系,DMAMUX其实就相当于是一个选择器,可以把外设的DMA请求映射到我们的DMA通道上(这么解释可能不对,但我觉得比较形象)。而且最重要的是,这个选择一共有115的外设DMA请求,也就是说这115个DMA请求每一个都可以映射到任何一个DMA通道上。这一点就比F4要方便了,我记得F4的DMA通道是固定的,G4乃至H7就要自由很多。 G4系列和H7、F4有一点不同的就是,G4没有数据流(STREAMS)这一说法,而是统一使用通道(CHANNELS)来代替,这一点可能会引起大家的误解,其实这俩是一样的。然后,DMAMUX一共是16条通道,0~7对应DMA1,8到15对应DMA2。
2. 代码编写
(1)串口部分的代码就不多说了,如下所示:
void my_uart_init(uint32_t baud){
__HAL_RCC_USART1_CLK_ENABLE();
UART_HandleTypeStr.Instance = USART1;
UART_HandleTypeStr.Init.BaudRate = baud;
UART_HandleTypeStr.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UART_HandleTypeStr.Init.ClockPrescaler = UART_PRESCALER_DIV1;
UART_HandleTypeStr.Init.Mode = UART_MODE_TX_RX;
UART_HandleTypeStr.Init.Parity = UART_PARITY_NONE;
UART_HandleTypeStr.Init.StopBits = UART_STOPBITS_1;
UART_HandleTypeStr.Init.WordLength = UART_WORDLENGTH_8B;
HAL_UART_Init(&UART_HandleTypeStr);
HAL_UART_Receive_IT(&UART_HandleTypeStr,receive_buf,sizeof(receive_buf));
}
(2)DMA初始化 其实DMA的初始化和我们之前的外设初始化很像: ① DMA1和DMAMUX的时钟; ② 使用__HAL_LINKDMA将外设和DMA通道链接起来; ③ 结构体配置; ④ DMA DeIint,这一步的目的是为了先复位DMA通道及其参数; ⑤ DMA Init,初始化DMA及其参数。 *⑥ 中断配置,这一步可以不配置,关于DMA中断又是一个比较复杂的话题了,这边就先不赘述了。
void my_dma_init(void){
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMAMUX1_CLK_ENABLE();
__HAL_LINKDMA(&UART_HandleTypeStr,hdmatx,DMA_HandleTypeStr);
DMA_HandleTypeStr.Instance = DMA1_Channel1;DMA_HandleTypeStr.Instance = DMA1_Channel1;
DMA_HandleTypeStr.Init.Direction = DMA_MEMORY_TO_PERIPH;
DMA_HandleTypeStr.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
DMA_HandleTypeStr.Init.MemInc = DMA_MINC_ENABLE;
DMA_HandleTypeStr.Init.Mode = DMA_NORMAL;
DMA_HandleTypeStr.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
DMA_HandleTypeStr.Init.PeriphInc = DMA_PINC_DISABLE;
DMA_HandleTypeStr.Init.Priority = DMA_PRIORITY_HIGH;
DMA_HandleTypeStr.Init.Request = DMA_REQUEST_USART1_TX;
if(HAL_DMA_DeInit(&DMA_HandleTypeStr) != HAL_OK)
while(1);
if(HAL_DMA_Init(&DMA_HandleTypeStr) != HAL_OK)
while(1);
}
这边就介绍几个参数: ① DMA_HandleTypeStr.Instance = DMA1_Channel1; 这个参数就是类似于F4或H7的STREAM的选择,DMA1、2各有6个通道选择。
② DMA_HandleTypeStr.Init.Mode = DMA_NORMAL; 这个模式的话,顾名思义,NORMAL也就是常规发送,发一次就停了,CIRCULAR也就是循环发送,CIRCULAR会一直不断地发送,传输一次后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错误。
③ DMA_HandleTypeStr.Init.Request = DMA_REQUEST_USART1_TX; 这个参数就很重要了,就是我们之前所说的115个外设DMA请求,我们选择需要的外设请求。
剩余的几个参数就不多提了,基本上和其他型号的32一样。
(3)主函数
int main(void)
{
HAL_Init();
my_system_clk_config();
uart_init(115200);
my_gpio_init();
my_dma_init();
printf("USART1 DMA TEST\n");
HAL_UART_Transmit_DMA(&UART_HandleTypeStr,send_data,sizeof(send_data));
while(1){
if(__HAL_DMA_GET_FLAG(&DMA_HandleTypeStr,DMA_FLAG_TC1)){
__HAL_DMA_CLEAR_FLAG(&DMA_HandleTypeStr,DMA_FLAG_TC1);
HAL_UART_DMAStop(&UART_HandleTypeStr);
}
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_4);
HAL_Delay(500);
}
}
测试的话,模仿正点原子的DMA测试程序,在while之前调用HAL_UART_Transmit_DMA函数进行发送,然后while循环里面闪灯,然后等待DMA发送完成标志位被置位后清空标志位,停止DMA发送。
以上就是工程的主要内容了,现象就不贴上来了,就是打印 “Hello World!” 在上位机上。
工程链接: 添加链接描述
3. 插入说明
不知道大家有没有过和我一样的疑问,HAL_DMA_Start和HAL_DMA_Start_IT这俩函数都没用到,那么这俩函数又是怎么去调用的呢?(没疑问的朋友自动忽略哈) 其实打开HAL_UART_Transmit_DMA函数体内如下所示 这边里面已经调用了HAL_DMA_Start_IT这个函数了,也就是说,我们调用HAL_UART_Transmit_DMA这个函数之后,HAL库是自动开启DMA中断的,但是我们一没有设置中断优先级(这个问题不大,32有默认的优先级,这个参考手册NVIC那一节),而没有定义中断服务函数(这个好像问题也不是很大,网上有人说会进入默认的中断服务函数,这个我也不知道,有待考究)但是真正的问题是,中断标志位没有清除!这边应用安富莱的手册中的一段话: 这边就说这么多了,DMA的中断系统相当复杂,我也不知道自己是不是完全搞懂了,如果有朋友明白的,欢迎批评指正😀。
|