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

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

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

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

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

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

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第1张图片

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

下面,将打开外部时钟源

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第2张图片

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

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第3张图片

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

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第4张图片

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

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第5张图片

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

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第6张图片

 开启中断

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第7张图片

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

选择DMA Settings点击Add,添加USART1_RXUSART1_TX

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第8张图片

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

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第9张图片

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第10张图片

 最后生成文件

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第11张图片

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

/* 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 */
}

实验结果

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)_第12张图片

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

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

你可能感兴趣的:(单片机,stm32,嵌入式硬件,python,c语言)