HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)

本文详细描述了如何在STM32单片机(如STM32F103ZET6)与Python之间通过串口进行通信,并利用DMA进行高效数据传输,包括设置系统时钟、配置USART1、启用中断以及使用DMA进行数据接收。后续章节将探讨Python端的数据接收和解析。
摘要由CSDN通过智能技术生成

最近想做一个控制电机的项目,其中会用到Pytho与单片机STM32之间的互同,最近也在看一些关于数据通信和拆包的相关知识,所以记录一下这段时间里对两者之间的互通所做的事情和发现的问题,以供自己和大家参考。

单片机的串口是我们常用的与电脑通信的外设,本次与Python互通就采用的串口实现上位机与下位机的通讯。

本章先讲解串口外设的使用,下一章讲解在Python中接收单片机发送的数据。

我采用的单片机型号是STM32F103ZET6,使用usart1进行数据的收发,所使用的引脚是PA9、PA10。使用STM32Cube打开串口进行初始化。

第一步,设置时钟源,在未设置的情况下,我们的单片机默认的系统时钟是8MHz,如下图所示。

所以,要想系统时钟达到最大就要使用外部晶振,不过,值得注意的是,F1系列的板子使用外部晶振作为时钟源时,系统时钟可超过72MHz,但是为了单片机的稳定性,我们对系统时钟的设置不能超过官方的限制。

下面,将打开外部时钟源

选择高速时钟源,然后选择72MHz,让系统自己设置。

第二步,在SYS里面的Debug选择 Serial write,非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器

第三步打开串口USART1,在USART1中的Mode选择 Asynchronous 异步通信

波特率为 115200 Bits/s。传输数据长度为 8 Bit。奇偶检验 None,停止位 1 ,接收和发送都使能

 开启中断

并且采用DMA进行数据传输,采用DMA 好处是不需要占用CPU的资源即可完成数据的接收和发送,极大的节约了CPU 的占用。对于DMA 的原理这里不重点解释,我们只需要知道他的功能和如何使用即可,下面将USART1的DMA开启。

选择DMA Settings点击Add,添加USART1_RXUSART1_TX

选择MDK-ARMCode Generator 中的 Generate peripheral initialization as a pair of '.c/.h' files per peripheral

 最后生成文件

生成的代码如下,这里说一个小技巧,我们可以在

/* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

中间写函数,例如

/* USER CODE BEGIN 1 */

      interesting();

  /* USER CODE END 1 */

这样,如果向开其他外设的时候直接在Cube里面打开,再生成即可,如果不卸载里面的话,我们自己写的代码将会在生成后被删除,会很麻烦。 

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();
  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 */
  }
  /* USER CODE END 3 */
}

usart.c

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA1_Channel5;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Channel4;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);
    HAL_DMA_DeInit(uartHandle->hdmatx);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

串口使能中断函数

void usart_dma_int()
{
	
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断

	HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//开启DMA接收
}

DMA发送函数

HAL_UART_Transmit_DMA(&huart1, buf,len)

重定向printf函数

int fputc(int ch,FILE *stream)
{
HAL_UART_Transmit(&huart1,( uint8_t *)&ch,1,0xFFFF);
return ch;
}

我们的接收数据采用DMA接收方式,使用DMA+IDLE空闲中断,这样的好处是,当接收到一串数据时,串口不会发生中断,而是将接收到的数据通过DMA存到缓存区中,当数据传输完成后,IDLE产生中断标志位,进而产生一次中断,我们就可以在这次中断中做一些我们想要实现的功能,这种DMA+IDLE接收的方式,很符合数据传输通讯的形式,代码如下

void USART1_IRQHandler(void)
{
        uint32_t flag = 0;
	    uint32_t num;//DMA没有传输的个数
	    flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
	if((tmp_flag != RESET))//IDE产生中断
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
		HAL_UART_DMAStop(&huart1); //停止DMA传输
		num = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数   
		rx_num =  BUFFER_SIZE - temp; //BUFFER_SIZE(接收数据缓存的最大个数)-num(剩余的个数)=当前接收的数据个数
		flag= 1;	// 接受完成标志位置1	
	}
  /* USER CODE END USART1_IRQn 0 */
   HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 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();
  /* USER CODE BEGIN 2 */
  usart_dma_int();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
     printf("实验\r\n");
     HAL_Delay(300);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

实验结果

 本次先讲解串口收发的使用,下一章介绍在Python中接收单片机发送的数据并解析。

跳转连接:https://blog.csdn.net/m0_73816319/article/details/135667240?spm=1001.2014.3001.5502

在使用STM32HAL库时,可以通过以下步骤实现串口DMA空闲中断: 1. 配置串口DMA传输,使其使用循环模式和使用空闲中断。 ```c /* Configure the DMA handler for Transmission process */ hdma_tx.Instance = USARTx_TX_DMA_STREAM; hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL; hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_tx.Init.Mode = DMA_NORMAL; hdma_tx.Init.Priority = DMA_PRIORITY_LOW; hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_tx.Init.MemBurst = DMA_MBURST_INC4; hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4; /* Initialize DMA handle */ if(HAL_DMA_Init(&hdma_tx) != HAL_OK) { Error_Handler(); } /* Associate the initialized DMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmatx, hdma_tx); /* Configure the DMA handler for reception process */ hdma_rx.Instance = USARTx_RX_DMA_STREAM; hdma_rx.Init.Channel = USARTx_RX_DMA_CHANNEL; hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_rx.Init.Mode = DMA_CIRCULAR; hdma_rx.Init.Priority = DMA_PRIORITY_HIGH; hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_rx.Init.MemBurst = DMA_MBURST_INC4; hdma_rx.Init.PeriphBurst = DMA_PBURST_INC4; /* Initialize DMA handle */ if(HAL_DMA_Init(&hdma_rx) != HAL_OK) { Error_Handler(); } /* Associate the initialized DMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmarx, hdma_rx); /* Configure the DMA handler callbacks */ HAL_DMA_RegisterCallback(&hdma_tx, HAL_DMA_XFER_CPLT_CB_ID, DMA_TxCpltCallback); HAL_DMA_RegisterCallback(&hdma_rx, HAL_DMA_XFER_CPLT_CB_ID, DMA_RxCpltCallback); HAL_DMA_RegisterCallback(&hdma_rx, HAL_DMA_XFER_ERROR_CB_ID, DMA_RxErrorCallback); /* Configure DMA transfer interrupts */ __HAL_DMA_ENABLE_IT(&hdma_rx, DMA_IT_TC); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_HT); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_FE); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_TE); ``` 2. 实现DMA传输完成回调函数。 ```c void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { /* Transmission complete callback */ } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Reception complete callback */ } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { /* UART error callback */ } ``` 3. 实现DMA空闲中断回调函数。 ```c void HAL_UART_IDLE_IRQHandler(UART_HandleTypeDef *huart) { /* Disable DMA transfer on reception */ hdma_rx.Instance->CR &= ~DMA_SxCR_EN; /* Clear IDLE flag */ __HAL_UART_CLEAR_IDLEFLAG(huart); /* Call user callback */ HAL_UART_RxCpltCallback(huart); /* Enable DMA transfer on reception */ hdma_rx.Instance->CR |= DMA_SxCR_EN; } ``` 4. 配置空闲中断使能。 ```c /* Enable the UART Idle Interrupt */ __HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE); ``` 通过以上步骤,就可以实现串口DMA空闲中断
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值