c - 让 DMA USART 在 STM32L053R8T6 上工作

标签 c stm32 dma usart

我在让我的计算机(准确地说是虚拟 COM 端口)通过 DMA 和 USART 与 STM32L053R8T6 (Nucleo) 板进行通信时遇到问题。这是我的 DMA 和 USART 部分的代码:

#include "Device/Include/stm32l0xx.h"   // Device header
#include "JB.h"
#include <string.h>

#define PCLK    32000000
#define BAUD    19200

uint8_t stringtosend[] = "test\n";
uint8_t stringtoreceive[] = " ";

void ENABLE_UART_DMA(void){
    RCC->AHBENR |= RCC_AHBENR_DMA1EN; //enable periph.clk for DMA1

    /**Enabling DMA for transmission
     * DMA1, Channel 4 mapped for USART2TX
     * USART2 TDR for peripheral address
     * stringtosend for data address
     * Memory increment, memory to peripheral | 8-bit transfer | transfer complete interrupt**/
    DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C4S) | (4 << (3 * 4));
    DMA1_Channel4->CPAR = (uint32_t)&(USART2->TDR);
    DMA1_Channel4->CMAR = (uint32_t)stringtosend;
    DMA1_Channel4->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE;

    /**Enabling DMA for reception
     * DMA1, Channel 5 mapped for USART2RX
     * USART2 RDR for peripheral address
     * stringtoreceive for data address
     * Data size given
     * Memory increment, peripheral to memory | 8-bit transfer | transfer complete interrupt**/
    DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C5S) | (4 << (4 * 4)); 
    DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR); 
    DMA1_Channel5->CMAR = (uint32_t)stringtoreceive;
    DMA1_Channel5->CNDTR = sizeof(stringtoreceive);
    DMA1_Channel5->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_EN;

    NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0); //NVIC enabled, max priority, channels 4-7
    NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
}

void CONFIGURE_UART_PARAM(void){
  RCC->IOPENR  |= ( 1ul <<  0); //Enable GPIOA clock
  RCC->APB1ENR |= ( 1ul << 17); //Enable USART#2 clock

  GPIOA->AFR[0] &= ~((15ul << 4* 3) | (15ul << 4* 2) ); //Clear PA2,PA3
  GPIOA->AFR[0] |=  (( 4ul << 4* 3) | ( 4ul << 4* 2) ); //Set   PA2,PA3
  GPIOA->MODER  &= ~(( 3ul << 2* 3) | ( 3ul << 2* 2) ); //Same as above
  GPIOA->MODER  |=  (( 2ul << 2* 3) | ( 2ul << 2* 2) );

    USART2->BRR = PCLK/BAUD;
    USART2->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; //Enable DMA mode in transmit and receive

    /*UART enabled for transmission and reception*/
  USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; 

  while((USART2->ISR & USART_ISR_TC) != USART_ISR_TC)
  { 
    /* add time out here for a robust application */
  }
  USART2->ICR = USART_ICR_TCCF;
}

void CONFIGURE_EXTI(void){
    SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
    EXTI->FTSR |= EXTI_FTSR_TR13; //falling edge trigger
    EXTI->IMR |= EXTI_IMR_IM13; //unmask
    NVIC_SetPriority(EXTI4_15_IRQn, 0); //def interrupt
    NVIC_EnableIRQ(EXTI4_15_IRQn);
}

/*************************************************************************************************************************************************************************************************************************/
/*************************************************************************************************************************************************************************************************************************/
/*Interrupt Handlers*/

void DMA1_Channel4_5_6_7IRQHandler(void){
    if((DMA1->ISR & DMA_ISR_TCIF4) == DMA_ISR_TCIF4){
    DMA1->IFCR = DMA_IFCR_CTCIF4; //Clear Channel 4 Transfer Complete flag
  }
  else if((DMA1->ISR & DMA_ISR_TCIF5) == DMA_ISR_TCIF5){
    DMA1->IFCR = DMA_IFCR_CTCIF5; //Clear Channel 5 Transfer Complete flag

    DMA1_Channel5->CCR &= ~DMA_CCR_EN;
    DMA1_Channel5->CNDTR = sizeof(stringtoreceive);/* Data size */
    DMA1_Channel5->CCR |= DMA_CCR_EN;
  }
}

void EXTI4_15_IRQHandler(void){
  if(!(GPIOC->IDR & (1 << 13))){
    /* Clear EXTI 13 flag */
    EXTI->PR = EXTI_PR_PIF13;

    /* start 8-bit transmission with DMA */
    DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
    DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
    DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
  }
}

//void EXTI4_15_IRQHandler(void){
//  if((EXTI->PR & EXTI_PR_PIF13) == EXTI_PR_PIF13){
//    /* Clear EXTI 13 flag */
//    EXTI->PR = EXTI_PR_PIF13;
//  
//    /* start 8-bit transmission with DMA */
//    DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
//    DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
//    DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
//  }
//}

现在,该特定代码基于 STM32L0 代码片段包 1.20“使用 DMA 的 USART/通信”中的示例。 USART 1 被简单地重新定义为 USART 2(因为这是虚拟 COM 端口使用的端口),并且 DMA channel 也据此重新定义。然而,这里的问题非常简单:它只会打印 stringtosend 一次(希望每次按下按钮 B1 时都执行一次),并且也不会通过 RX 接收数据 - 就好像它完全忽略 DMA 中断处理程序一样 - 我是这样的我不确定如何测试(该板上没有可用的跟踪功能)。我所拥有的似乎很好地反射(reflect)了引用手册,所有主要内容是:

int main(){

SystemCoreClockInit();
CONFIGURE_UART_PARAM();
ENABLE_UART_DMA();
pushbutton_def();
CONFIGURE_EXTI();

while(1){
}

...它应该只对定义的中断使用react,但是它没有,并且对于我的一生,我不明白为什么。如果你能帮助我,我会很高兴 - 我也想避免 HAL 或 LL API - 这不是一个足够复杂的项目,无法保证它们的使用(多个输入、输出、两个板之间通过 USART/DMA 进行通信),另外我会更喜欢学习更接近寄存器级别的工作。

谢谢!

编辑(响应贝伦迪的建议): 1. GPIOC在另一个文件中定义,用pushbutton_def()调用:

RCC->IOPENR |= (1UL << 2); //enable GPIOC
  • 我完全理解你的解释的意思(事实上,这两个引用的寄存器是“相同的”,0x00000020U),但我不确定如何重新定义它:这是我之后的尝试查看引用手册(SYSCFG部分)和源代码(仍然不起作用):

    SYSCFG->EXTICR[3] = ((SYSCFG->EXTICR[3] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC);

  • 按照建议,我已将 USART2->ICR = USART_ICR_TCCF; 添加到 EXTIhandler,位于 DMA channel 之后。我将其保留在 USART 定义中。不过,该消息仍然只发送一次。

  • 最佳答案

    GPIOC 未启用

    这里,

    RCC->IOPENR  |= ( 1ul <<  0); //Enable GPIOA clock
    

    您也应该启用GPIOC

    EXTI13 映射到 PA13

    这里,

    SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
    

    您正在设置EXTI0-EXTI3的配置寄存器,实际上将EXTI1映射到PC1 EXTI13 仍然映射到 PA13,它实际上是 SWDIO,连接到板载调试器。我猜想 SWDIO 上的流量会触发 EXTI 中断,处理程序会检查 PC13(由于端口被禁用而始终读取 0),并启用 DMA。不过 DMA 传输只能工作一次,因为

    USART_ISR_TC在中断中没有被清除

    但仅在启动时一次。您应该移动此行

    USART2->ICR = USART_ICR_TCCF;
    

    到 EXTI 中断处理程序。

    我不确定为什么接收不起作用,也许 DMA 处理程序没有机会运行,因为 EXTI 不断被 SWD 流量重新触发。两个中断具有相同的优先级,中断号较小的中断优先,即 EXTI 处理程序。如果它总是在完成之前重新触发,那么它将被再次调用,而不是让其他处理程序运行。

    关于c - 让 DMA USART 在 STM32L053R8T6 上工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49602503/

    相关文章:

    c - 为什么无法在关键部分调用 freeRTOS API?

    无法在STM32上使用DAC和DMA生成正弦波

    Linux 中断不由包装器驱动程序处理

    linux-kernel - mmap 可以处理 linux 驱动程序中的非连续页面范围吗?

    c++ - 如何禁用 Dev C++ 中的提示?

    c - 如何在我的 C 限制下检查用户的有效输入?

    embedded - STM32 SPI 慢速计算

    c++ - 重新启动后位域会出现硬故障

    c - 指针在 printf() 中不起作用

    c - 如何制作包含日期和两个字符串的字符串