STM32 Cubemax(四) —— STM32利用DMA空闲中断与Openmv通信


前言

因为之前电赛使用到了Openmv作为摄像头,处理完数据后将数据传回STM32主控来进行后续的操作,也看了很多论坛上的文章,但总感觉代码结构都不是自己的风格,以此记录一下。

本文章STM32的配置,按照我之前的风格,都为从Cubemax配置生成。

零、连线

OpenMvSTM32
TX(P4)RX
GNDGND

一、OpenMv代码

首先介绍一下OpenMv这边的代码
主要使用的是ustruct这个库来作为串口的发送,注意,如果是直接用OpenMv的串口write发送,单片机这边收到的数据是不好处理的。

首先OpenMv也是单片机,要对其串口进行配置。

from pyb import UART
uart = UART(3,115200)   #定义串口3变量
uart.init(115200, bits=8, parity=None, stop=1) # 这里串口设置要和后面stm32的对应

主要发送函数为如下

def sending_data(cx,cy,cw,ch):
   global uart;
   """
   Format     C Type     Python    字节数
     b     signed char   integer     1
     h         short     integer     2
     i         int       integer     4
   """
   data = ustruct.pack("<bbhhhhb",            #格式为俩个字符俩个短整型(2字节)
                  0x2C,                       #帧头1
                  0x12,                       #帧头2
                  int(cx), # up sample by 4   #数据1
                  int(cy), # up sample by 4   #数据2
                  int(cw), # up sample by 4   #数据1
                  int(ch), # up sample by 4   #数据2
                  0x5B)                       #帧尾
   uart.write(data);   #必须要传入一个字节数组

二、CubeMax配置

首先简单的时钟树配置,就不在这里讲了,只要按照自己使用的单片机芯片进行配置就好,如果不会的话,可以看我之前的文章。

1.串口及DMA配置

这里主要讲讲串口及DMA的配置

这里我使用的是串口3,这里的配置要和上面OpenMv对应上

在这里插入图片描述
开启中断
在这里插入图片描述
开启接收DMA
在这里插入图片描述
配置完成后就可以生成代码了。

三、STM32单片机代码编写

首先在生成usart.c的MX_USART3_UART_Init中加入DMA接收中断及空闲中断

当然也可以把开启初始化放在别的地方。


/* USART3 init function */

void MX_USART3_UART_Init(void)
{

  /* USER CODE BEGIN USART3_Init 0 */

  /* USER CODE END USART3_Init 0 */

  /* USER CODE BEGIN USART3_Init 1 */

  /* USER CODE END USART3_Init 1 */
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART3_Init 2 */
  // 加入DMA空闲中断
	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);  
	HAL_UART_Receive_DMA(&huart3,OpennMv_Data.RxBuffer,RXBUFFER_LEN);  
  /* USER CODE END USART3_Init 2 */

}

创建接收结构体

#define RXBUFFER_LEN 20

typedef struct User_USART
{
		uint8_t Rx_flag;									
		uint8_t Rx_len;							//接收长度			
		uint8_t frame_head[2];					//帧头		
		uint8_t frame_tail;						//帧尾			
		int x,y,w,h;							//处理数据
		uint8_t RxBuffer[RXBUFFER_LEN];			//接收缓存
}User_USART;

在stm32f4xx_it.c中编写串口3中断

/**
  * @brief This function handles USART3 global interrupt.
  */
void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */
  	// 空闲中断处理
	uint32_t temp_flag = 0;
	uint32_t temp;
	temp_flag = __HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE);
	if((temp_flag!=RESET))																
	{
			__HAL_UART_CLEAR_IDLEFLAG(&huart3);
			temp = huart3.Instance->SR;   										
			temp = huart3.Instance->DR; 										
			HAL_UART_DMAStop(&huart3);   									
			temp = hdma_usart3_rx.Instance->NDTR; 	
			//F1的板子	temp = hdma_usart3_rx.Instance->CNDTR; 				
			OpennMv_Data.Rx_len = RXBUFFER_LEN-temp;  				
			OpenMvData_Process(OpennMv_Data.RxBuffer);					//按照自己需求改写这个函数						
			OpennMv_Data.Rx_flag = 1;   											
	}
	HAL_UART_Receive_DMA(&huart3,OpennMv_Data.RxBuffer,RXBUFFER_LEN);	
  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */

  /* USER CODE END USART3_IRQn 1 */
}

构造处理数据处理函数


//结构体初始化 
void User_USART_Init(User_USART *Data)
{
		for(uint8_t i=0; i < RXBUFFER_LEN; i++)	Data->RxBuffer[i] = 0;
		Data->frame_head[0] = 0x2C;
		Data->frame_head[1] = 0x12;
		Data->frame_tail = 0x5B;
		Data->Rx_flag = 0;
		Data->Rx_len = 0;
		Data->x = 0;
		Data->y = 0;
		Data->w = 0;
		Data->h = 0;
}

void OpenMvData_Process(uint8_t *RxBuffer)
{
		//判断帧头帧尾
		if(RxBuffer[0] != OpennMv_Data.frame_head[0]) return;
		if(RxBuffer[1] != OpennMv_Data.frame_head[1]) return;
		if(RxBuffer[10] != OpennMv_Data.frame_tail) return;
		
		OpennMv_Data.x = (RxBuffer[3]<<8)|RxBuffer[2];
		OpennMv_Data.y = (RxBuffer[5]<<8)|RxBuffer[4];
		OpennMv_Data.w = (RxBuffer[7]<<8)|RxBuffer[6];
		OpennMv_Data.h = (RxBuffer[9]<<8)|RxBuffer[8];
}

我在OpenMv中发送如下数据

   i = i+1
   sending_data(500,255,i,4)
   print("is sending:",500,255,i,4)
   pyb.delay(1000)

在单片机中成功接收并处理
在这里插入图片描述

代码总和

下面放一下usart.c和my_usart.c,my_usart.h的全部代码作为参考
uart.c

/**
  ******************************************************************************
  * @file    usart.c
  * @brief   This file provides code for the configuration
  *          of the USART instances.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "usart.h"
#include "my_usart.h"

/* USER CODE BEGIN 0 */
extern User_USART OpennMv_Data;
/* USER CODE END 0 */

UART_HandleTypeDef huart3;
DMA_HandleTypeDef hdma_usart3_rx;

/* USART3 init function */

void MX_USART3_UART_Init(void)
{

  /* USER CODE BEGIN USART3_Init 0 */

  /* USER CODE END USART3_Init 0 */

  /* USER CODE BEGIN USART3_Init 1 */

  /* USER CODE END USART3_Init 1 */
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART3_Init 2 */
	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);  //开启空闲中断
	HAL_UART_Receive_DMA(&huart3,OpennMv_Data.RxBuffer,RXBUFFER_LEN);  //开启DMA的接收
  /* USER CODE END USART3_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

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

  /* USER CODE END USART3_MspInit 0 */
    /* USART3 clock enable */
    __HAL_RCC_USART3_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**USART3 GPIO Configuration
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* USART3 DMA Init */
    /* USART3_RX Init */
    hdma_usart3_rx.Instance = DMA1_Stream1;
    hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart3_rx.Init.Mode = DMA_NORMAL;
    hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart3_rx);

    /* USART3 interrupt Init */
    HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspInit 1 */

  /* USER CODE END USART3_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspDeInit 0 */

  /* USER CODE END USART3_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART3_CLK_DISABLE();

    /**USART3 GPIO Configuration
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);

    /* USART3 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);

    /* USART3 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspDeInit 1 */

  /* USER CODE END USART3_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

my_usart.c

#include "my_usart.h"

User_USART OpennMv_Data;		//接收数据

//初始化函数
void User_USART_Init(User_USART *Data)
{
		for(uint8_t i=0; i < RXBUFFER_LEN; i++)	Data->RxBuffer[i] = 0;
		Data->frame_head[0] = 0x2C;
		Data->frame_head[1] = 0x12;
		Data->frame_tail = 0x5B;
		Data->Rx_flag = 0;
		Data->Rx_len = 0;
		Data->x = 0;
		Data->y = 0;
		Data->w = 0;
		Data->h = 0;
}

void OpenMvData_Process(uint8_t *RxBuffer)
{
		//检查帧头帧尾
		if(RxBuffer[0] != OpennMv_Data.frame_head[0]) return;
		if(RxBuffer[1] != OpennMv_Data.frame_head[1]) return;
		if(RxBuffer[10] != OpennMv_Data.frame_tail) return;
		
		OpennMv_Data.x = (RxBuffer[3]<<8)|RxBuffer[2];
		OpennMv_Data.y = (RxBuffer[5]<<8)|RxBuffer[4];
		OpennMv_Data.w = (RxBuffer[7]<<8)|RxBuffer[6];
		OpennMv_Data.h = (RxBuffer[9]<<8)|RxBuffer[8];
}

my_usart.h

#ifndef _MY_USART_H
#define _MY_USART_H

#include "main.h"

#define RXBUFFER_LEN 20

typedef struct User_USART
{
		uint8_t Rx_flag;									//接收完成标志
		uint8_t Rx_len;										//接收长度
		uint8_t frame_head[2];						//帧头
		uint8_t frame_tail;								//帧尾
		int x,y,w,h;
		uint8_t RxBuffer[RXBUFFER_LEN];		//数据存储
}User_USART;

void OpenMvData_Process(uint8_t *RxBuffer);
void User_USART_Init(User_USART *Data);
#endif


总结

在实际操作过程中,有时会出现一些小问题,如OpenMv处理数据给stm32发送时,会出现干扰信号,先检查两者有没有共地,再检查软件编写问题。

这里简单说一下为什么要用空闲中断的方法处理,在实际操作中,OpenMv发送的数据很快,而其中也会含有杂乱信息,通过空闲中断的方法,可以便于快速处理掉异常信息。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐