基于HAL库的stm32f103c8t6 串口通信实验(DMA方式)

目录

 

一、 DMA(Directional Memory Access)简介

二、 STM32CubeMX环境配置

   1.  选择stm32f03c8t6芯片

         2.  配置RCC

          3. 配置sys

          4. 配置UART1

   5. 配置DMA 

       ​编辑   

   6.  配置时钟

       7. 工程配置

 三、DMA函数介绍

         1. DMA初始化函数

         2. DMA发送函数

        3. DMA接收函数

 四、程序思路和编写

  1. 程序思路 

 2. 程序编写

五、运行效果和仿真时序

       实物效果:

       仿真:

六、总结与参考


 

一、 DMA(Directional Memory Access)简介

      DMA,直接存储器访问,是一种完全由硬件执行数据交换的工作方式。它由DMA控制器控制在存储器和存储器,存储器和外设之间的批量数据传输。DMA 传输方式无需 CPU 直接 控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备 开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。

549332c294414ca69e566396c4ff8cfd.png

     DMA能够直接不占用CPU任何资源,直接将存储器和外设连接,让二者相互访问。

STM32 最多有 2 个 DMA 控制器(DMA2 仅存在大容量产品中),DMA1 有 7 个通道。DMA2 有 5 个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器
来协调各个 DMA 请求的优先权。
STM32 的 DMA 有以下一些特性:
●  每个通道都直接连接专用的硬件 DMA 请求,每个通道都同样支持软件触发。这些功能
通过软件来配置。
 
●  在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如
在相等优先权时由硬件决定(请求 0 优先于请求 1,依此类推) 。
 
●  独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和
目标地址必须按数据传输宽度对齐。
 
●  支持循环的缓冲器管理
 
●  每个通道都有 3 个事件标志(DMA 半传输,DMA 传输完成和 DMA 传输出错),这 3 个
事件标志逻辑或成为一个单独的中断请求。
 
●  存储器和存储器间的传输
 
●  外设和存储器,存储器和外设的传输
 
●  闪存、SRAM、外设的 SRAM、APB1 APB2 和 AHB 外设均可作为访问的源和目标。
 
●  可编程的数据传输数目:最大为 65536
 
STM32F103ZET6 有两个 DMA 控制器,DMA1 和 DMA2

DMA1的通道对应外设:

4667c290954e4e48ab691ef84e320f3e.png

此实验需要用到USART1DMA方式串口通信,那么就需要开启DMA的通道4、5

二、 STM32CubeMX环境配置

       

   1.  选择stm32f03c8t6芯片

0f78bb15158149009d0471dab7a785d9.png

         2.  配置RCC

51253c5c996f475da0d72c27108e2a88.png

          3. 配置sys

34d0c22ba06a413b87bdb2e848eba8b9.png

          4. 配置UART1

6113098f54424707b4ebb3050f0f4ebd.png

1892bde9772048ceafed645945df758e.png

   5. 配置DMA 

       315478ebe6b343f79d94038863a8d826.png   

af52fcf5b6f349d9849476884ec09703.png 215243c5ba6c4602802f77e2cec47e7d.png

 

   6.  配置时钟

86536071cf9649d0a3de44597c0bfa2b.png

       7. 工程配置

2297c5f129624f6ea7d897fc17976905.png

 7a6401756faf4bb1ae5368cccd3e9493.png 最后点击生成代码即可 

 三、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)

0ce41bb1e70643c486ba2c5da8474528.png

该函数是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); 
	}
}

五、运行效果和仿真时序

       实物效果:

26000319c2424dd28ed3160ce3d1ad97.gif

       仿真:

         仿真查看时序和波特率

         修改一下主函数的循环

  while (1)
  {

		  HAL_UART_Transmit_DMA(&huart1,(uint8_t *)SendBuff,16);
			HAL_Delay(1000);
}

    仿真查看波形需要修改一下源码,直接在while1中调用流水灯程序,查看电平持续时间,仿真需要的MDK配置:

ac0a2abb1c0f40fabd543f7b39a5c405.png

  f0d60eae12f94911b572131ce51aafed.png

 波形:

9b04559217e449c9abfeb6cd86e6c3a8.png

 

六、总结与参考

         对比前面的查询和中断方式,如果传输的数据量过大,那么就会一直触发中断,从而导致中断连续发生,CPU同样也需要花费大量时间去频繁地处理中断,DMA将外设和内存直接连接,不经过CPU,直接与外界交换数据,这样就节省了CPU资源,从而提高了效率。

        参考:

【精选】HAL库 STM32CubeMX实现串口DMA发送接收_stm32 dma和uart-CSDN博客

 

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值