嵌入式大厂面经 STM32 DMA常见面试题(持续更新中!)
这是一个嵌入式大厂面试题专栏,每天更新高频面试题。专栏将包含题目描述、详细解析、相关知识点扩展以及实际代码示例。内容涵盖操作系统、驱动开发、通信协议等核心领域,并结合实际项目经验进行分析。每道题目都会附带面试官可能的追问方向,帮助大家更好地准备面试!
STM32 DMA常见面试题
DMA(直接内存访问)是STM32微控制器中非常重要的外设,能够显著提高系统性能。以下是STM32 DMA相关的常见面试题及解答:
1. DMA的基本概念
什么是DMA?
DMA(Direct Memory Access)是一种无需CPU干预即可在外设和内存之间、或内存与内存之间传输数据的技术。DMA控制器可以独立于CPU工作,从而释放CPU资源用于其他任务。
DMA的主要优势
- 减轻CPU负担,提高系统效率
- 数据传输速度快,实时性好
- 适合大量数据传输场景
- 降低功耗(相比CPU轮询方式)
2. STM32 DMA架构
STM32 DMA控制器特点
不同系列STM32的DMA控制器有所不同:
- STM32F1系列:2个DMA控制器(DMA1/DMA2)每个控制器有7个通道通道优先级可配置
- STM32F4系列:2个DMA控制器每个控制器有8个数据流(Stream)每个数据流可以配置为8个通道请求之一支持双缓冲区模式和FIFO
// STM32F4 DMA控制器结构 typedef struct { DMA_Stream_TypeDef *Stream; // DMA数据流 uint32_t Channel; // DMA通道 uint32_t PeripheralBaseAddr; // 外设地址 uint32_t MemoryBaseAddr; // 内存地址 uint32_t Direction; // 传输方向 uint32_t BufferSize; // 缓冲区大小 uint32_t PeripheralInc; // 外设地址增量模式 uint32_t MemoryInc; // 内存地址增量模式 uint32_t PeripheralDataSize; // 外设数据宽度 uint32_t MemoryDataSize; // 内存数据宽度 uint32_t Mode; // 工作模式 uint32_t Priority; // 优先级 } DMA_InitTypeDef;
3. DMA传输模式
基本传输模式
- 存储器到外设:从内存读取数据发送到外设
- 外设到存储器:从外设接收数据存储到内存
- 存储器到存储器:在两个内存区域之间传输数据(部分STM32系列支持)
工作模式
- 正常模式:传输完成后停止
- 循环模式:传输完成后自动重新开始传输
- 双缓冲模式:使用两个内存缓冲区交替工作(F4及以上系列支持)
// 配置DMA工作模式示例 void DMA_ModeConfig(void) { // 正常模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 循环模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // STM32F4双缓冲模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)Buffer1, (uint32_t)Buffer2); DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE); }
4. DMA配置参数详解
关键配置参数
- 外设/内存地址:数据源和目标地址
- 数据方向:外设到内存、内存到外设、内存到内存
- 传输数据大小:要传输的数据项数量
- 地址增量模式:地址是否在每次传输后自动增加
- 数据宽度:8位、16位或32位
- 优先级:低、中、高、超高
- 传输模式:正常、循环等
- 中断使能:传输完成、半传输完成、错误中断等
// STM32F1 DMA基本配置示例 void DMA_BasicConfig(void) { // 使能DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 配置DMA通道 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR; // 外设地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer; // 内存地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 数据方向 DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; // 缓冲区大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据宽度 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 工作模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 非内存到内存模式 DMA_Init(DMA1_Channel3, &DMA_InitStructure); // 使能DMA通道 DMA_Cmd(DMA1_Channel3, ENABLE); }
5. DMA与外设结合应用
UART与DMA结合
// UART DMA发送配置 void UART_DMA_TxConfig(void) { // 配置DMA DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_Init(DMA1_Channel4, &DMA_InitStructure); // USART1_TX对应DMA1_Channel4 // 使能UART DMA发送请求 USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); // 使能DMA通道 DMA_Cmd(DMA1_Channel4, ENABLE); } // UART DMA接收配置 void UART_DMA_RxConfig(void) { // 配置DMA DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式接收 DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_Init(DMA1_Channel5, &DMA_InitStructure); // USART1_RX对应DMA1_Channel5 // 使能UART DMA接收请求 USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); // 使能DMA通道 DMA_Cmd(DMA1_Channel5, ENABLE); }
ADC与DMA结合
// ADC DMA配置 void ADC_DMA_Config(void) { // 配置DMA DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = ADC_CHANNEL_NUM; // 多通道采样 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_Init(DMA1_Channel1, &DMA_InitStructure); // ADC1对应DMA1_Channel1 // 使能DMA通道 DMA_Cmd(DMA1_Channel1, ENABLE); // 配置ADC DMA模式 ADC_DMACmd(ADC1, ENABLE); }
SPI与DMA结合
// SPI DMA发送配置 void SPI_DMA_TxConfig(void) { // 配置DMA DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI_TxBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitSt
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
嵌入式面试八股文全集 文章被收录于专栏
这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。