打印
[STM32F0]

学习笔记+STM32F0系列的STM32Cube固件例程 DMA

[复制链接]
1359|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
slotg|  楼主 | 2019-12-23 23:20 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 slotg 于 2019-12-24 08:06 编辑

学习笔记+适用于STM32F0系列的STM32Cube固件例程


DMA_FLASHToRAM

在固件库 Projects\STM32F030R8-Nucleo\Examples\DMA\DMA_FLASHToRAM 里面的这一个例程:



这个例程是将内部 FLASH 的数据使用 DMA 的方式传输到 RAM 内,我直接编译后下载到  STM32F030R8-Nucleo 开发板后运行就看到板载的 LD2 亮起!嗯,发生了什么事?看看 main 回圈:

int main(void)
{
  /* STM32F0xx HAL library initialization:
       - Configure the Flash prefetch
       - Systick timer is configured by default as source of time base, but user
         can eventually implement his proper time base source (a general purpose
         timer for example or other time source), keeping in mind that Time base
         duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and
         handled in milliseconds basis.
       - Low Level Initialization
     */
  HAL_Init();

  /* Configure the system clock to 48 MHz */
  SystemClock_Config();

  /* Initialize LED */
  BSP_LED_Init(LED2);

  /* Set to 1 if an transfer error is detected */
  transferErrorDetected = 0;

  /* Configure and enable the DMA channel for Memory to Memory transfer */
  DMA_Config();

  /* Infinite loop */
  while (1)
  {
    if (transferErrorDetected == 1)
    {
      /* Toggle LED2 with a period of 200 ms */
      BSP_LED_Toggle(LED2);
      HAL_Delay(200);
    }
  }
}

流程中将 transferErrorDetected 变数设定为 0,在 while (1) 回圈中当 transferErrorDetected 变数为 1 时 LD2 会以 200ms 的周期闪烁。然而在 DMA_Config() 函数里做了那些事情呢?

/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  Configure the DMA controller according to the parameters
  *         defined in main.h file
  * [url=home.php?mod=space&uid=536309]@NOTE[/url]  This function is used to :
  *        -1- Enable DMA1 clock
  *        -2- Select the DMA functional Parameters
  *        -3- Select the DMA instance to be used for the transfer
  *        -4- Initialize the DMA channel
  *        -5- Select Callbacks functions called after Transfer complete and
  *            Transfer error interrupt detection
  *        -6- Configure NVIC for DMA transfer complete/error interrupts
  *        -7- Start the DMA transfer using the interrupt mode
  * @param  None
  * @retval None
  */
static void DMA_Config(void)
{
  /*## -1- Enable DMA1 clock #################################################*/
  __HAL_RCC_DMA1_CLK_ENABLE();

  /*##-2- Select the DMA functional Parameters ###############################*/
  DmaHandle.Init.Direction = DMA_MEMORY_TO_MEMORY;          /* M2M transfer mode                */
  DmaHandle.Init.PeriphInc = DMA_PINC_ENABLE;               /* Peripheral increment mode Enable */
  DmaHandle.Init.MemInc = DMA_MINC_ENABLE;                  /* Memory increment mode Enable     */
  DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* Peripheral data alignment : Word */
  DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;    /* memory data alignment : Word     */
  DmaHandle.Init.Mode = DMA_NORMAL;                         /* Normal DMA mode                  */
  DmaHandle.Init.Priority = DMA_PRIORITY_HIGH;              /* priority level : high            */

  /*##-3- Select the DMA instance to be used for the transfer : DMA1_Channel1 #*/
  DmaHandle.Instance = DMA_INSTANCE;

  /*##-4- Initialize the DMA channel ##########################################*/
  if (HAL_DMA_Init(&DmaHandle) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }
  
  /*##-5- Select Callbacks functions called after Transfer complete and Transfer error */
  HAL_DMA_RegisterCallback(&DmaHandle, HAL_DMA_XFER_CPLT_CB_ID, TransferComplete);
  HAL_DMA_RegisterCallback(&DmaHandle, HAL_DMA_XFER_ERROR_CB_ID, TransferError);

  /*##-6- Configure NVIC for DMA transfer complete/error interrupts ##########*/
  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriority(DMA_INSTANCE_IRQ, 0, 0);

  /* Enable the DMA global Interrupt */
  HAL_NVIC_EnableIRQ(DMA_INSTANCE_IRQ);

  /*##-7- Start the DMA transfer using the interrupt mode ####################*/
  /* Configure the source, destination and buffer size DMA fields and Start DMA transfer */
  /* Enable All the DMA interrupts */
  if (HAL_DMA_Start_IT(&DmaHandle, (uint32_t)&aSRC_Const_Buffer, (uint32_t)&aDST_Buffer, BUFFER_SIZE) != HAL_OK)
  {
    /* Transfer Error */
    Error_Handler();
  }
}

这里以 7 个步骤清楚的说明了 DMA 的设定流程,程式在做好 DMA 的相关设定后将 FLASH 内的数组 aSRC_Const_Buffer[] 传输到 RAM 内的数组 aDST_Buffer[]。程序设定了 2 个回调函数,当传输成功 (HAL_DMA_XFER_CPLT_CB_ID) 时调用 TransferComplete() 函数,传输失败 (HAL_DMA_XFER_ERROR_CB_ID) 时调用 TransferError() 函数。

回调函数 TransferComplete() 内点亮 LD2。
static void TransferComplete(DMA_HandleTypeDef *DmaHandle)
{
  /* Turn LED2 on: Transfer correct */
  BSP_LED_On(LED2);
}

回调函数 TransferError() 内设定 transferErrorDetected 变数为 1,该变数在 main() 主回圈里让 LD2 闪烁。
static void TransferError(DMA_HandleTypeDef *DmaHandle)
{
  transferErrorDetected = 1;
}


所以当程序下载运行后 LD2 也就点亮了起来。


使用特权

评论回复
沙发
slotg|  楼主 | 2019-12-23 23:33 | 只看该作者
UART_TwoBoards_ComDMA

在固件库 Projects\STM32F030R8-Nucleo\Examples\UART\UART_TwoBoards_ComDMA 里面的这一个例程:



这个例程是 UART 串口使用 DMA 的方式传输数据,在编译后下载到开发板后看到板载 LD2 快速闪烁,按下使用者按键 B1 后 LD2 熄灭,嗯,然后呢?

main 回圈进入后等待按下使用者按键 B1,此时 LD2 快闪,按键按下后 LD2 熄灭。
  /* Configure User push-button in Interrupt mode */
  BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
  
  /* Wait for User push-button press before starting the Communication.
     In the meantime, LED2 is blinking */
  while(UserButtonStatus == 0)
  {
      /* Toggle LED2*/
      BSP_LED_Toggle(LED2);
      HAL_Delay(100);
  }
  
  BSP_LED_Off(LED2);
接下来设定 USART,例程中使用了 USART1:
  /*##-1- Configure the UART peripheral ######################################*/
  /* Put the USART peripheral in the Asynchronous mode (UART Mode) */
  /* UART configured as follows:
      - Word Length = 8 Bits
      - Stop Bit = One Stop bit
      - Parity = None
      - BaudRate = 9600 baud
      - Hardware flow control disabled (RTS and CTS signals) */

  UartHandle.Instance        = USARTx;
  UartHandle.Init.BaudRate   = 9600;
  UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
  UartHandle.Init.StopBits   = UART_STOPBITS_1;
  UartHandle.Init.Parity     = UART_PARITY_NONE;
  UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
  UartHandle.Init.Mode       = UART_MODE_TX_RX;
  UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

  if(HAL_UART_DeInit(&UartHandle) != HAL_OK)
  {
    Error_Handler();
  }  
  if(HAL_UART_Init(&UartHandle) != HAL_OK)
  {
    Error_Handler();
  }
串口使用 DMA 方式接收数据:
  /*##-2- Program the Reception process #####################################*/  
  if(HAL_UART_Receive_DMA(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)
  {
    Error_Handler();
  }
串口使用 DMA 方式发送数据,并启动发送:
  /*##-3- Start the transmission process #####################################*/  
  /* While the UART in reception process, user can transmit data through
     "aTxBuffer" buffer */
  if(HAL_UART_Transmit_DMA(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE)!= HAL_OK)
  {
    Error_Handler();
  }
等待发送结束,并 RESET UartReady 状态:
  /*##-4- Wait for the end of the transfer ###################################*/  
  while (UartReady != SET)
  {
  }

  /* Reset transmission flag */
  UartReady = RESET;
接下来判断串口是否有数据接收?
  /*##-5- Wait for the end of the transfer ###################################*/  
  while (UartReady != SET)
  {
  }

  /* Reset transmission flag */
  UartReady = RESET;
在串口发送结束的回调函数会设置 UartReady 为 SET 状态:
/**
  * @brief  Tx Transfer completed callback
  * @param  UartHandle: UART handle.
  * @note   This example shows a simple way to report end of DMA Tx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  /* Set transmission flag: trasfer complete*/
  UartReady = SET;

}
在串口接收完成的回调函数也会设置 UartReady 为 SET 状态:
/**
  * @brief  Rx Transfer completed callback
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report end of DMA Rx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  /* Set transmission flag: trasfer complete*/
  UartReady = SET;
  
}
因此假如没有接收到串口数据的话程序就会停在第5阶段,也就是 LD2 为熄灭状态。假如串口有接收到数据的话,接下来会去判断接收到的数据跟发送的数据是否相同?假如相同的话会点亮 LD2:

  /*##-6- Compare the sent and received buffers ##############################*/
  if(Buffercmp((uint8_t*)aTxBuffer,(uint8_t*)aRxBuffer,RXBUFFERSIZE))
  {
    Error_Handler();
  }
   
  /* Turn on LED2 if test passes then enter infinite loop */
  BSP_LED_On(LED2);
   
  /* Infinite loop */
  while (1)
  {
  }
USART1_TX 在 PA9 管脚,USART1_RX 在 PA10 管脚,我将 2 支管脚连接在一起后重新运行程序,在按下使用者按键 B1 后 LD2 就亮起了。



我在 PA9 管脚接上一个 USB 转串口的转换板,在电脑上察看串口发送了那些数据:






使用特权

评论回复
板凳
renzheshengui| | 2020-1-17 14:20 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
地板
wakayi| | 2020-1-17 14:28 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
5
wowu| | 2020-1-17 14:38 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
6
xiaoqizi| | 2020-1-17 14:48 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
7
木木guainv| | 2020-1-17 15:06 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
8
guanjiaer| | 2020-1-17 15:22 | 只看该作者
非常感谢分享

使用特权

评论回复
9
heimaojingzhang| | 2020-1-17 15:26 | 只看该作者
非常感谢分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

38

主题

1177

帖子

6

粉丝