在上节文章已经完成了对GPIO的配置, 以及对一些必要的DMA函数进行了讲解
(新手向详解)通过STM32CubeMx使用STM32F411的DMA发送数据翻转GPIO-CSDN博客
接下来将会完成对DMA中断设置的工作, 以及相关函数的理解
HAL_StatusTypeDef HAL_DMA_Start_IT
(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
HAL_StatusTypeDef status = HAL_OK;
/* calculate DMA base and stream number */
DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma->StreamBaseAddress;
/* Check the parameters */
assert_param(IS_DMA_BUFFER_SIZE(DataLength));
/* Process locked */
__HAL_LOCK(hdma);
if(HAL_DMA_STATE_READY == hdma->State)
{
/* Change DMA peripheral state */
hdma->State = HAL_DMA_STATE_BUSY;
/* Initialize the error code */
hdma->ErrorCode = HAL_DMA_ERROR_NONE;
/* Configure the source, destination address and the data length */
DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);
/* Clear all interrupt flags at correct offset within the register */
regs->IFCR = 0x3FU << hdma->StreamIndex;
/* Enable Common interrupts*/
hdma->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;
if(hdma->XferHalfCpltCallback != NULL)
{
hdma->Instance->CR |= DMA_IT_HT;
}
/* Enable the Peripheral */
__HAL_DMA_ENABLE(hdma);
}
else
{
/* Process unlocked */
__HAL_UNLOCK(hdma);
/* Return error status */
status = HAL_BUSY;
}
return status;
}
此函数可以以中断的方式开启DMA传输
如果传输完成 , 中断就会触发 , 属于事件中断的一种
但是现在有一个问题
我们并没有给DMA配置中断
CubeMx配置
生成代码

void DMA2_Stream0_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream0_IRQn 0 */
/* USER CODE END DMA2_Stream0_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_memtomem_dma2_stream0);
/* USER CODE BEGIN DMA2_Stream0_IRQn 1 */
/* USER CODE END DMA2_Stream0_IRQn 1 */
}
这是中断向量表直接映射过来的函数
发生DMA中断后 , CPU是先去中断向量表里面 , 找到了DMA2的
![]()
再从此处跳转
跳转到这里
回调函数:
HAL_StatusTypeDef HAL_DMA_RegisterCallback
(DMA_HandleTypeDef hdma, HAL_DMA_CallbackIDTypeDef CallbackID, void ( pCallback)(DMA_HandleTypeDef *_hdma))
HAL_StatusTypeDef HAL_DMA_RegisterCallback(DMA_HandleTypeDef *hdma, HAL_DMA_CallbackIDTypeDef CallbackID, void (* pCallback)(DMA_HandleTypeDef *_hdma))
{
HAL_StatusTypeDef status = HAL_OK;
/* Process locked */
__HAL_LOCK(hdma);
if(HAL_DMA_STATE_READY == hdma->State)
{
switch (CallbackID)
{
case HAL_DMA_XFER_CPLT_CB_ID:
hdma->XferCpltCallback = pCallback;
break;
case HAL_DMA_XFER_HALFCPLT_CB_ID:
hdma->XferHalfCpltCallback = pCallback;
break;
case HAL_DMA_XFER_M1CPLT_CB_ID:
hdma->XferM1CpltCallback = pCallback;
break;
case HAL_DMA_XFER_M1HALFCPLT_CB_ID:
hdma->XferM1HalfCpltCallback = pCallback;
break;
case HAL_DMA_XFER_ERROR_CB_ID:
hdma->XferErrorCallback = pCallback;
break;
case HAL_DMA_XFER_ABORT_CB_ID:
hdma->XferAbortCallback = pCallback;
break;
default:
/* Return error status */
status = HAL_ERROR;
break;
}
}
else
{
/* Return error status */
status = HAL_ERROR;
}
/* Release Lock */
__HAL_UNLOCK(hdma);
return status;
}
感兴趣可以慢慢研究 , 粗略来讲
此函数的作用就是 , 先找到DMA的句柄 , 然后再通过你给的DMA句柄注册一个回调
这个回调的类型
typedef enum
{
HAL_DMA_XFER_CPLT_CB_ID = 0x00U, /*!< Full transfer */
HAL_DMA_XFER_HALFCPLT_CB_ID = 0x01U, /*!< Half Transfer */
HAL_DMA_XFER_M1CPLT_CB_ID = 0x02U, /*!< M1 Full Transfer */
HAL_DMA_XFER_M1HALFCPLT_CB_ID = 0x03U, /*!< M1 Half Transfer */
HAL_DMA_XFER_ERROR_CB_ID = 0x04U, /*!< Error */
HAL_DMA_XFER_ABORT_CB_ID = 0x05U, /*!< Abort */
HAL_DMA_XFER_ALL_CB_ID = 0x06U /*!< All */
}HAL_DMA_CallbackIDTypeDef;
如果你想要一个传输完成后的回调
那么就把这个参数传到 CallbackID的形参位置
HAL_DMA_XFER_CPLT_CB_ID
最后一个形参是你要传入的回调函数 , 就是前面设置的, 当DMA传输完成后 , 你要进入哪个回调函数
代码实战:

void my_dma_TC_callback(DMA_HandleTypeDef *_hdma)
{
}

/* USER CODE BEGIN 2 */
HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0,
HAL_DMA_XFER_CPLT_CB_ID,
my_dma_TC_callback); my_dma_TC_callback);
/* USER CODE END 2 */
while (1)
{
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0, (uint32_t)data_gpio[counter % 2] , (uint32_t)&GPIOA->ODR , 1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
那么想要在中断里持续反转
就只用定义一个变量
然后对2取余
uint32_t counter = 0 ;
void my_dma_TC_callback(DMA_HandleTypeDef *_hdma)
{
counter ++ ;
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0, (uint32_t)data_gpio[counter % 2] , (uint32_t)&GPIOA->ODR , 1);
}
这样偶数时 取余是高电平 , 奇数时取余是低电平 , 然后循环调用, 就实现了高低电平转换

那么这样反转有什么用呢?
那么不妨假设数组变大

这样GPIO就可以按照你想要的任何时序去变化 , 这只是一个基础 , 输出的波形变化可以利用在各种领域各种地方
比如说ADC上电的时候要去写寄存器 , 就可以交给DMA去同步并发

426

被折叠的 条评论
为什么被折叠?



