目录
一、 DMA(Directional Memory Access)简介
一、 DMA(Directional Memory Access)简介
DMA,直接存储器访问,是一种完全由硬件执行数据交换的工作方式。它由DMA控制器控制在存储器和存储器,存储器和外设之间的批量数据传输。DMA 传输方式无需 CPU 直接 控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备 开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
DMA能够直接不占用CPU任何资源,直接将存储器和外设连接,让二者相互访问。
DMA1的通道对应外设:
此实验需要用到USART1DMA方式串口通信,那么就需要开启DMA的通道4、5
二、 STM32CubeMX环境配置
1. 选择stm32f03c8t6芯片
2. 配置RCC
3. 配置sys
4. 配置UART1
5. 配置DMA
6. 配置时钟
7. 工程配置
最后点击生成代码即可
三、DMA函数介绍
1. DMA初始化函数
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,DMA_InitTypeDef* DMA_InitStruct)
该函数有两个参数,第一参数是DMA通道结构体,第二个参数是DMA_InitTypeDefDMA初始化结构体,该结构体定义为:
typedef struct
{
uint32_t Direction;//传输方向,例如存储器到外设 DMA_MEMORY_TO_PERIPH
uint32_t PeriphInc;//外设(非)增量模式,非增量模式 DMA_PINC_DISABLE
uint32_t MemInc;//存储器(非)增量模式,增量模式 DMA_MINC_ENABLE
uint32_t PeriphDataAlignment; //外设数据大小:8/16/32 位。
uint32_t MemDataAlignment; //存储器数据大小:8/16/32 位。
uint32_t Mode;//模式:外设流控模式/循环模式/普通模式
uint32_t Priority; //DMA 优先级:低/中/高/非常高
}DMA_InitTypeDef;
2. DMA发送函数
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
该函数是DMA发送函数,一共三个参数,第一个参数是指明哪个串口,第二个参数是发送的数据,为8bit,一个字符形式,第三个参数是发送数据的大小。
如果用串口1用DMA方式发送1个字符,则可以写:
ch='a';
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)ch,1);
3. DMA接收函数
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
接收函数同样也是三个参数,将大小为size的接收数据保存到pData中,如:
char ReBuff[5];
HAL_UART_Receive_DMA(&huart1,(uint8_t *)Rebuff,5);
四、程序思路和编写
1. 程序思路
STM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送。
单片机首先需要开启接收中断,即调用HAL_UART_Receive_IT函数,当单片机接收到数据完成后,就调用中断回调函数HAL_UART_RxCpltCallback,可以编写逻辑,判断接收的字符是否为start,如果是就通过中断给PC机发送一个字符start,并置flag=1(在主函数循环中,flag=1,每隔1s给PC发送一次“Hello Windows!\n”),如果是stop,置flag=0。
2. 程序编写
1. 主函数编写:
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();
MX_USART1_UART_Init();
HAL_UART_Receive_DMA(&huart1, (uint8_t *)&ReBuff,5);
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
if(flag==1)
{
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)SendBuff,17);
HAL_Delay(1000);
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
2. 字符匹配函数
int StringCompare(char rcData[5],char rcData2[5]){
for(uint8_t i = 0 ; i < 5 ; i++){
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
3. 回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
if(StringCompare(ReBuff,start)==1)
{
flag=1;
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)start,5);
}
else if(StringCompare(ReBuff,stop)==1)
{
flag=0;
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)stop,5);
}
HAL_UART_Receive_DMA(&huart1, (uint8_t *)&ReBuff,5);
}
}
五、运行效果和仿真时序
实物效果:
仿真:
仿真查看时序和波特率
修改一下主函数的循环
while (1)
{
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)SendBuff,16);
HAL_Delay(1000);
}
仿真查看波形需要修改一下源码,直接在while1中调用流水灯程序,查看电平持续时间,仿真需要的MDK配置:
波形:
六、总结与参考
对比前面的查询和中断方式,如果传输的数据量过大,那么就会一直触发中断,从而导致中断连续发生,CPU同样也需要花费大量时间去频繁地处理中断,DMA将外设和内存直接连接,不经过CPU,直接与外界交换数据,这样就节省了CPU资源,从而提高了效率。
参考:
【精选】HAL库 STM32CubeMX实现串口DMA发送接收_stm32 dma和uart-CSDN博客