在STM32H7 MCU上配置DMA请求多路复用器

2019-07-14 17:58发布

我最近使用新生产的SM32H7 MCU,目前正在将一些旧代码迁移到它们(我想看看他们如何处理一些需要更高速度的应用程序)。H7具有DMA请求多路复用器,在我曾经使用的旧F7,F4或F3中不存在。在后一种模型中,通过向DMA_x Stream_y控制寄存器输入正确的值来完成DMA通道映射。例如DMA2_Stream3->CR |= (0x3 << DMA_SxCR_CHSEL_Pos);将为DM2 Stream3选择第三个通道(在F7 MCU的情况下,这将对应于SPI1 TX DMA请求: 1.png 据我所知,DMA流到DMA通道映射不再是“硬连线”,它可以在H7系列中手动重新配置。正如手册所述,必须使用DMAMux1将DMA请求线路连接到DMA通道。不幸的是,DMAMUX配置在参考手册中描述得相当差。我还没有意识到如何准确DMA流,外设和DMA通道通过多路复用器互连。下面是一段代码,理想情况下应该是
  • 设置SPI1。
  • 设置DMA。

  • 启用DMA流以进行SPI TX传输
    1. <font size="4">RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN;   // Enable usage of GPIOA
    2. RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

    3. GPIOA->MODER &= ~GPIO_MODER_MODER5;
    4. GPIOA->MODER |= GPIO_MODER_MODER5_1;   // Alternate function for SPI1 SCK on PA5
    5. GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5;   // High Speed on PA5
    6. GPIOA->AFR[0] |= (0x05 << 5 * 4);   // AFRL selected AF5 (SPI1 SCK) for PA5

    7. GPIOA->MODER &= ~GPIO_MODER_MODER6;
    8. GPIOA->MODER |= GPIO_MODER_MODER6_1;   // Alternate function for SPI1 MISO on PA6
    9. GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6;   // High Speed on PA6
    10. GPIOA->AFR[0] |= (0x05 << 6 * 4);   // AFRL selected AF5 (SPI1 MISO) for PA6

    11. GPIOA->MODER &= ~GPIO_MODER_MODER7;
    12. GPIOA->MODER |= GPIO_MODER_MODER7_1;   // Alternate function for SPI1 MOSI on PA7
    13. GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7;   // High Speed on PA7
    14. GPIOA->AFR[0] |= (0x05 << 7 * 4);   // AFRL selected AF5 (SPI1 MOSI) for PA7

    15. GPIOA->MODER &= ~GPIO_MODER_MODER4;
    16. GPIOA->MODER |= GPIO_MODER_MODER4_1;   // Alternate function for SPI1 NSS on PA7
    17. GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4;   // High Speed on PA7
    18. GPIOA->AFR[0] |= (0x05 << 4 * 4);   // AFRL selected AF5 (SPI1 NSS) for PA7

    19. GPIOA->PUPDR |= GPIO_PUPDR_PUPDR4_0;  // Ensure all pull up pull down resistors are enabled
    20. GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5;   // Ensure all pull up pull down resistors are disabled
    21. GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6;   // Ensure all pull up pull down resistors are disabled
    22. GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7;   // Ensure all pull up pull down resistors are disabled

    23. SPI1->CFG1 = (1u << SPI_CFG1_MBR_Pos) | // Master baud rate: master clock / 2
    24.                  (7u << SPI_CFG1_CRCSIZE_Pos) | // Length of CRC frame
    25.                  SPI_CFG1_TXDMAEN | SPI_CFG1_RXDMAEN | // Enable RX/TX DMA
    26.                  (7u << SPI_CFG1_FTHLV_Pos) | // FIFO threshold level
    27.                  (7u << SPI_CFG1_DSIZE_Pos) //Number of bits in at single SPI data frame
    28.                  ;

    29. SPI1->CFG2 = SPI_CFG2_SSOE | // SS output enable
    30.              SPI_CFG2_MASTER // SPI Master
    31.              ;      

    32. RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;   // DMA2 clock enable;

    33. DMA2_Stream3->CR = 0u;
    34. DMA2_Stream3->PAR = (uint32_t) &(SPI1->TXDR);
    35. DMA2_Stream3->M0AR = (uint32_t) &(Data_Buffer[0]);
    36. DMA2_Stream3->CR |= (1u << DMA_SxCR_DIR_Pos);
    37. DMA2_Stream3->CR |= DMA_SxCR_MINC;
    38. DMA2_Stream3->CR |= DMA_SxCR_PL;
    39. DMA2_Stream3->NDTR = 1000;

    40. // 5. Use DMAMux1 to route a DMA request line to the DMA channel.
    41. DMAMUX1_Channel0->CCR  = (37u << DMAMUX_CxCR_DMAREQ_ID_Pos);

    42. SPI1->CR1 |= SPI_CR1_SPE;
    43. DMA2_Stream3->CR |= DMA_SxCR_EN;</font>
    复制代码
    此代码编译,我可以将其上传到STM32H753ZIT6 MCU。完整的代码还有PLL设置,它不包含在上面的代码段中(PLL初始化工作得很好,因为我能够探测MCO引脚上的400 MHz系统时钟)。DMA多路复用器设置无疑是不完整的。我只选择了一个适当的多路复用器输入资源。我甚至不确定多路复用器通道是否正确(或者他们的选择在H7 MCU中是否是任意的)。所以我的问题是为SPI TX传输设置DMA多路复用器的正确方法是什么?如果我可以运行这个MWE,我或多或少能够完成我的其余代码的迁移。
    所以,我一直在尝试关注user9403409的建议,但不幸的是,我无法走得太远。我仍然无法让SPI在H7系列微控制器上通过DMA工作。现在我可以让SPI在没有H7的DMA的情况下正常工作:
    1. <font size="4">#include "stm32h7xx.h"

    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <string.h>

    5. static void InitializeMCO(void);
    6. static void ConfigureHSI(void);
    7. static void InitializeMasterTxSPI(void);
    8. uint8_t s_TransferBuffer[10];

    9. int main()
    10. {        
    11.     s_TransferBuffer[0] = 0xAA;
    12.     s_TransferBuffer[1] = 0xBB;
    13.     s_TransferBuffer[2] = 0xCC;

    14.     ConfigureHSI();
    15.     InitializeMCO();
    16.     InitializeMasterTxSPI();
    17.     while(1){};
    18. }

    19. static void ConfigureHSI(void)
    20. {
    21.     PWR->CR3 |= PWR_CR3_SCUEN;
    22.     PWR->D3CR |= (PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0);
    23.         while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY)
    24.         {
    25.         };

    26.     FLASH->ACR = FLASH_ACR_LATENCY_2WS;

    27.     RCC->CR |= RCC_CR_HSION;
    28.     while ((RCC->CR & RCC_CR_HSIRDY) != RCC_CR_HSIRDY)
    29.     {
    30.     };

    31.     RCC->PLLCKSELR = (4u << RCC_PLLCKSELR_DIVM1_Pos) |
    32.                          (32u << RCC_PLLCKSELR_DIVM2_Pos) |
    33.                          (32u << RCC_PLLCKSELR_DIVM3_Pos) |
    34.                          RCC_PLLCKSELR_PLLSRC_HSI
    35.                          ;

    36.     RCC->PLLCFGR   =  RCC_PLLCFGR_DIVR1EN |
    37.                       RCC_PLLCFGR_DIVQ1EN |
    38.                       RCC_PLLCFGR_DIVP1EN |
    39.                       (2u << RCC_PLLCFGR_PLL1RGE_Pos)  |
    40.                       (1u << RCC_PLLCFGR_PLL1VCOSEL_Pos)
    41.                       ;

    42.     RCC->PLL1DIVR = ((2u - 1u) << RCC_PLL1DIVR_R1_Pos) |         
    43.         ((2u - 1u) << RCC_PLL1DIVR_Q1_Pos) |
    44.         ((2u - 1u) << RCC_PLL1DIVR_P1_Pos) |
    45.         ((10u - 1u) << RCC_PLL1DIVR_N1_Pos)  // Reducing the clock rate so I can probe it with my slow USB scope
    46.         ;

    47.     RCC->D1CFGR = RCC_D1CFGR_D1CPRE_DIV1;
    48.     RCC->D1CFGR = RCC_D1CFGR_HPRE_DIV2 |
    49.                   RCC_D1CFGR_D1PPRE_DIV2;
    50.     RCC->D2CFGR = RCC_D2CFGR_D2PPRE1_DIV2 |
    51.                   RCC_D2CFGR_D2PPRE2_DIV2;
    52.     RCC->D3CFGR = RCC_D3CFGR_D3PPRE_DIV2;

    53.     RCC->CR |= RCC_CR_PLL1ON;
    54.     while (!(RCC->CR & RCC_CR_PLLRDY))
    55.     {
    56.     };

    57.     RCC->CFGR |= (1u << 25);
    58.     RCC->CFGR |= RCC_CFGR_SW_PLL1;
    59.     while (!(RCC->CFGR & RCC_CFGR_SWS_PLL1))
    60.     {
    61.     };
    62. }

    63. /* Displays MCO on PC9 */
    64. static void InitializeMCO(void)
    65. {
    66.     RCC->CFGR |= RCC_CFGR_MCO2;
    67.         RCC->CFGR |= (15 << 25); // Reducing the output so I can probe it with my slow USB scope

    68.     RCC->AHB4ENR &= ~RCC_AHB4ENR_GPIOCEN;
    69.     RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN;

    70.     GPIOC->MODER &= ~GPIO_MODER_MODER9;
    71.     GPIOC->MODER |= GPIO_MODER_MODER9_1;

    72.     GPIOC->OTYPER &= ~GPIO_OTYPER_OT_9;
    73.     GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR9;

    74.     GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR9;
    75.     GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;

    76.     GPIOC->AFR[0] &= ~GPIO_AFRL_AFRL0;
    77. }

    78. static void InitializeMasterTxSPI(void)
    79. {
    80.     RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN;   // Enable usage of GPIOA

    81.     GPIOA->MODER &= ~GPIO_MODER_MODER5;
    82.     GPIOA->MODER |= GPIO_MODER_MODER5_1;   // Alternate function for SPI1 SCK on PA5
    83.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5;   // High Speed on PA5
    84.     GPIOA->AFR[0] |= (0x05 << 5 * 4);   // AFRL selected AF5 (SPI1 SCK) for PA5

    85.     GPIOA->MODER &= ~GPIO_MODER_MODER6;
    86.     GPIOA->MODER |= GPIO_MODER_MODER6_1;   // Alternate function for SPI1 MISO on PA6
    87.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6;   // High Speed on PA6
    88.     GPIOA->AFR[0] |= (0x05 << 6 * 4);   // AFRL selected AF5 (SPI1 MISO) for PA6

    89.     GPIOA->MODER &= ~GPIO_MODER_MODER7;
    90.     GPIOA->MODER |= GPIO_MODER_MODER7_1;   // Alternate function for SPI1 MOSI on PA7
    91.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7;   // High Speed on PA7
    92.     GPIOA->AFR[0] |= (0x05 << 7 * 4);   // AFRL selected AF5 (SPI1 MOSI) for PA7

    93.     GPIOA->MODER &= ~GPIO_MODER_MODER4;
    94.     GPIOA->MODER |= GPIO_MODER_MODER4_1;   // Alternate function for SPI1 NSS on PA7
    95.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4;   // High Speed on PA7
    96.     GPIOA->AFR[0] |= (0x05 << 4 * 4);   // AFRL selected AF5 (SPI1 NSS) for PA7

    97.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR4;  // Ensure all pull up pull down resistors are enabled
    98.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5;   // Ensure all pull up pull down resistors are disabled
    99.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6;   // Ensure all pull up pull down resistors are disabled
    100.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7;   // Ensure all pull up pull down resistors are disabled

    101.     RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

    102.     SPI1->CR1 = 0;

    103.     SPI1->CFG1 = (3u << SPI_CFG1_MBR_Pos) |
    104.                  (7u << SPI_CFG1_CRCSIZE_Pos) |
    105.                  //SPI_CFG1_TXDMAEN | // SPI_CFG1_RXDMAEN |
    106.                  (7u << SPI_CFG1_FTHLV_Pos) |
    107.                  (7u << SPI_CFG1_DSIZE_Pos)
    108.                  ;

    109.     SPI1->CFG2 = SPI_CFG2_SSOE |
    110.                  SPI_CFG2_MASTER
    111.                  ;      

    112.     SPI1->CR2 |= 3;
    113.     SPI1->CR1 |= SPI_CR1_SPE;
    114.     SPI1->CR1 |= SPI_CR1_CSTART;

    115.     for (uint32_t i=0; i<3; i++)
    116.     {
    117.             while ((SPI1->SR & SPI_SR_TXP) != SPI_SR_TXP){};
    118.             *((__IO uint32_t *)&SPI1->TXDR) = *((uint32_t *)&s_TransferBuffer[i]);
    119.     }
    120. }</font>
    复制代码
    上面的代码基本上做了三件事:
    • ConfigureHSI 初始化HSI时钟(我已经降低了时钟速度,以便能够使用我目前拥有的慢速USB示波器进行一些探测)。
    • InitializeMCO 显示主时钟输出(只是为了确保时钟配置正确)。
    • InitializeMasterTxSPI 配置SPI并发出三字节消息。

    我绝对可以看到在我的示波器上发送的消息: 2.png 时基为200 ns / div,供参考。
    另一方面,如果我尝试通过DMA重做整个事情,我看不到任何输出。这就是我的基于DMA的SPI代码:
    1. <font size="4">#include "stm32h7xx.h"
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <string.h>

    5. static void InitializeMCO(void);
    6. static void ConfigureHSI(void);
    7. static void InitializeDMA(void);
    8. static void InitializeMasterTxSPI(void);
    9. uint8_t s_TransferBuffer[10];

    10. int main()
    11. {        
    12.     s_TransferBuffer[0] = 0xAA;
    13.     s_TransferBuffer[1] = 0xBB;
    14.     s_TransferBuffer[2] = 0xCC;

    15.     ConfigureHSI();
    16.     InitializeMCO();
    17.     InitializeDMA();
    18.     InitializeMasterTxSPI();
    19.     while(1){};
    20. }

    21. /* Initializes the MCU clock */
    22. static void ConfigureHSI(void)
    23. {
    24.     PWR->CR3 |= PWR_CR3_SCUEN;
    25.     PWR->D3CR |= (PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0);
    26.         while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY)
    27.         {
    28.         };

    29.     FLASH->ACR = FLASH_ACR_LATENCY_2WS;

    30.     RCC->CR |= RCC_CR_HSION;
    31.     while ((RCC->CR & RCC_CR_HSIRDY) != RCC_CR_HSIRDY)
    32.     {
    33.     };

    34.     RCC->PLLCKSELR = (4u << RCC_PLLCKSELR_DIVM1_Pos) |
    35.                          (32u << RCC_PLLCKSELR_DIVM2_Pos) |
    36.                          (32u << RCC_PLLCKSELR_DIVM3_Pos) |
    37.                          RCC_PLLCKSELR_PLLSRC_HSI
    38.                          ;

    39.     RCC->PLLCFGR   =  RCC_PLLCFGR_DIVR1EN |
    40.                       RCC_PLLCFGR_DIVQ1EN |
    41.                       RCC_PLLCFGR_DIVP1EN |
    42.                       (2u << RCC_PLLCFGR_PLL1RGE_Pos)  |
    43.                       (1u << RCC_PLLCFGR_PLL1VCOSEL_Pos)
    44.                       ;

    45.     RCC->PLL1DIVR = ((2u - 1u) << RCC_PLL1DIVR_R1_Pos) |         
    46.         ((2u - 1u) << RCC_PLL1DIVR_Q1_Pos) |
    47.         ((2u - 1u) << RCC_PLL1DIVR_P1_Pos) |
    48.         ((10u - 1u) << RCC_PLL1DIVR_N1_Pos)  // Reducing the clock rate so I can probe it with my slow USB scope
    49.         ;

    50.     RCC->D1CFGR = RCC_D1CFGR_D1CPRE_DIV1;
    51.     RCC->D1CFGR = RCC_D1CFGR_HPRE_DIV2 |
    52.                   RCC_D1CFGR_D1PPRE_DIV2;
    53.     RCC->D2CFGR = RCC_D2CFGR_D2PPRE1_DIV2 |
    54.                   RCC_D2CFGR_D2PPRE2_DIV2;
    55.     RCC->D3CFGR = RCC_D3CFGR_D3PPRE_DIV2;

    56.     RCC->CR |= RCC_CR_PLL1ON;
    57.     while (!(RCC->CR & RCC_CR_PLLRDY))
    58.     {
    59.     };

    60.     RCC->CFGR |= (1u << 25);
    61.     RCC->CFGR |= RCC_CFGR_SW_PLL1;
    62.     while (!(RCC->CFGR & RCC_CFGR_SWS_PLL1))
    63.     {
    64.     };
    65. }

    66. /* Displays MCO on PC9 */
    67. static void InitializeMCO(void)
    68. {
    69.     RCC->CFGR |= RCC_CFGR_MCO2;
    70.     RCC->CFGR |= (15 << 25); // Reducing the output so I can probe it with my slow USB scope

    71.     RCC->AHB4ENR &= ~RCC_AHB4ENR_GPIOCEN;
    72.     RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN;

    73.     GPIOC->MODER &= ~GPIO_MODER_MODER9;
    74.     GPIOC->MODER |= GPIO_MODER_MODER9_1;

    75.     GPIOC->OTYPER &= ~GPIO_OTYPER_OT_9;
    76.     GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR9;

    77.     GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR9;
    78.     GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;

    79.     GPIOC->AFR[0] &= ~GPIO_AFRL_AFRL0;
    80. }

    81. static void InitializeDMA()
    82. {
    83.     RCC->AHB2ENR |= (0x7 << 29);  // Enable the SRAM
    84.     RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;   // DMA1 clock enable;

    85.     // Set the peripheral and memory addresses:
    86.     DMA1_Stream0->PAR = *((__IO uint32_t *)&SPI1->TXDR);
    87.     DMA1_Stream0->M0AR = *((uint32_t *)&s_TransferBuffer[0]);

    88.     DMA1_Stream0->CR = 0u;
    89.     DMA1_Stream0->CR |= (1u << DMA_SxCR_DIR_Pos); // Memory to peripheral
    90.     DMA1_Stream0->CR |= DMA_SxCR_MINC; // Memory increment mode
    91.     DMA1_Stream0->CR |= (3u << DMA_SxCR_PL_Pos); // Very high priority

    92.     DMA1_Stream0->NDTR = 3; // Number of data

    93.     DMAMUX1_Channel0->CCR  = (38u << DMAMUX_CxCR_DMAREQ_ID_Pos);

    94. }

    95. static void InitializeMasterTxSPI(void)
    96. {
    97.     RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN;   // Enable usage of GPIOA

    98.     GPIOA->MODER &= ~GPIO_MODER_MODER5;
    99.     GPIOA->MODER |= GPIO_MODER_MODER5_1;   // Alternate function for SPI1 SCK on PA5
    100.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5;   // High Speed on PA5
    101.     GPIOA->AFR[0] |= (0x05 << 5 * 4);   // AFRL selected AF5 (SPI1 SCK) for PA5

    102.     GPIOA->MODER &= ~GPIO_MODER_MODER6;
    103.     GPIOA->MODER |= GPIO_MODER_MODER6_1;   // Alternate function for SPI1 MISO on PA6
    104.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6;   // High Speed on PA6
    105.     GPIOA->AFR[0] |= (0x05 << 6 * 4);   // AFRL selected AF5 (SPI1 MISO) for PA6

    106.     GPIOA->MODER &= ~GPIO_MODER_MODER7;
    107.     GPIOA->MODER |= GPIO_MODER_MODER7_1;   // Alternate function for SPI1 MOSI on PA7
    108.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7;   // High Speed on PA7
    109.     GPIOA->AFR[0] |= (0x05 << 7 * 4);   // AFRL selected AF5 (SPI1 MOSI) for PA7

    110.     GPIOA->MODER &= ~GPIO_MODER_MODER4;
    111.     GPIOA->MODER |= GPIO_MODER_MODER4_1;   // Alternate function for SPI1 NSS on PA4
    112.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4;   // High Speed on PA4
    113.     GPIOA->AFR[0] |= (0x05 << 4 * 4);   // AFRL selected AF5 (SPI1 NSS) for PA4

    114.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR4;  // Ensure all pull up pull down resistors are enabled
    115.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5;   // Ensure all pull up pull down resistors are disabled
    116.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6;   // Ensure all pull up pull down resistors are disabled
    117.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7;   // Ensure all pull up pull down resistors are disabled

    118.     RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

    119.     SPI1->CR1 = 0;
    120.     SPI1->CFG1 = (3u << SPI_CFG1_MBR_Pos) |
    121.                  (7u << SPI_CFG1_CRCSIZE_Pos) |
    122.                  SPI_CFG1_TXDMAEN | // SPI_CFG1_RXDMAEN |
    123.                  (7u << SPI_CFG1_FTHLV_Pos) |
    124.                  (7u << SPI_CFG1_DSIZE_Pos)
    125.                  ;
    126.     SPI1->CFG2 = SPI_CFG2_SSOE |
    127.                  SPI_CFG2_MASTER
    128.                  ;      

    129.     //SPI1->CR2 |= 3;
    130.     SPI1->CR1 |= SPI_CR1_SPE;
    131.     SPI1->CR1 |= SPI_CR1_CSTART;

    132.     DMA1_Stream0->CR |= DMA_SxCR_EN;
    133. }</font>
    复制代码本质上,它是一回事,唯一的区别是有一个InitializeDMA功能,DMA传输是通过DMA1_Stream0->CR |= DMA_SxCR_EN命令启动的(就像之前的MCU系列中的情况一样)。所以,遗憾的是,我仍然无法通过H7上的DMA启动SPI。任何帮助将不胜感激。








友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
6条回答
hzp_bbs1
1楼-- · 2019-07-15 04:54
你能详细说明吗?这不是: // 5. Use DMAMux1 to route a DMA request line to the DMA channel. DMAMUX1_Channel0->CCR = (37u << DMAMUX_CxCR_DMAREQ_ID_Pos); 行吗?
sjjs001
2楼-- · 2019-07-15 05:42
 精彩回答 2  元偷偷看……
hzp_bbs1
3楼-- · 2019-07-15 10:55
好的,我会尽快尝试重建这个项目。与此同时,您能否解释DMAMUX-CxCR寄存器中的SYNC_ID位和请求发生器寄存器(DMAMUX_RGxCR)?什么时候需要使用那些?此外,如何将DMA流“附加”到特定外设?在我上面的例子中,我选择了DMA2_Stream3和DMAMUX1_Channel0,但实际上没有一段代码将两者连接在一起。
sjjs001
4楼-- · 2019-07-15 14:37
只是dmamux1_channel0硬连线到dma1_stream0,dmamux1_channel8到dma2_stream0等等。它需要从一个事件源同步启动多个dma。
hzp_bbs1
5楼-- · 2019-07-15 17:11
我设法让SPI DMA运行。在下面发布我的工作代码:
  1. #include "stm32h7xx.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>

  5. static void InitializeMCO(void);
  6. static void ConfigureHSI(void);
  7. static void InitializeDMA(void);
  8. static void InitializeMasterTxSPI(void);
  9. const uint8_t aTxBuffer[] = "Simple SPI message";

  10. int main()
  11. {
  12.     ConfigureHSI();
  13.     InitializeMCO();
  14.     InitializeDMA();
  15.     InitializeMasterTxSPI();

  16.     while (1)
  17.     {
  18.             /* Delay added to distinguish between the SPI messages: */
  19.             while(DMA2_Stream4->NDTR != 0) asm("nop");
  20.             for(uint32_t i=0; i<0xBF; i++) asm("nop");

  21.             //DMA2_Stream4->CR &= ~DMA_SxCR_EN;
  22.             DMA2->HIFCR |= DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4;
  23.             //DMA2_Stream4->PAR = (uint32_t) &(SPI1->TXDR);
  24.             DMA2_Stream4->M0AR = (uint32_t ) &(aTxBuffer[0]);
  25.             DMA2_Stream4->NDTR = 0x12;

  26.             DMA2_Stream4->CR |= DMA_SxCR_EN;
  27.     };
  28. }

  29. /* Initializes the MCU clock */
  30. static void ConfigureHSI(void)
  31. {
  32.     PWR->CR3 |= PWR_CR3_SCUEN;
  33.     PWR->D3CR |= (PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0);
  34.     while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY)
  35.     {
  36.     };

  37.     FLASH->ACR = FLASH_ACR_LATENCY_2WS;

  38.     RCC->CR |= RCC_CR_HSION;
  39.     while ((RCC->CR & RCC_CR_HSIRDY) != RCC_CR_HSIRDY)
  40.     {
  41.     };

  42.     RCC->PLLCKSELR = (4u << RCC_PLLCKSELR_DIVM1_Pos) |
  43.                          (32u << RCC_PLLCKSELR_DIVM2_Pos) |
  44.                          (32u << RCC_PLLCKSELR_DIVM3_Pos) |
  45.                          RCC_PLLCKSELR_PLLSRC_HSI;

  46.     RCC->PLLCFGR = RCC_PLLCFGR_DIVR1EN |
  47.                        RCC_PLLCFGR_DIVQ1EN |
  48.                        RCC_PLLCFGR_DIVP1EN |
  49.                        (2u << RCC_PLLCFGR_PLL1RGE_Pos) |
  50.                        (1u << RCC_PLLCFGR_PLL1VCOSEL_Pos);

  51.     RCC->PLL1DIVR = ((2u - 1u) << RCC_PLL1DIVR_R1_Pos) |
  52.                         ((2u - 1u) << RCC_PLL1DIVR_Q1_Pos) |
  53.                         ((2u - 1u) << RCC_PLL1DIVR_P1_Pos) |
  54.                         ((10u - 1u) << RCC_PLL1DIVR_N1_Pos)   // Reducing the clock rate so I can probe it with my slow USB scope
  55.             ;

  56.     RCC->D1CFGR = RCC_D1CFGR_D1CPRE_DIV1;
  57.     RCC->D1CFGR = RCC_D1CFGR_HPRE_DIV2 | RCC_D1CFGR_D1PPRE_DIV2;
  58.     RCC->D2CFGR = RCC_D2CFGR_D2PPRE1_DIV2 | RCC_D2CFGR_D2PPRE2_DIV2;
  59.     RCC->D3CFGR = RCC_D3CFGR_D3PPRE_DIV2;

  60.     RCC->CR |= RCC_CR_PLL1ON;
  61.     while (!(RCC->CR & RCC_CR_PLLRDY))
  62.     {
  63.     };

  64.     RCC->CFGR |= (1u << 25);
  65.     RCC->CFGR |= RCC_CFGR_SW_PLL1;
  66.     while (!(RCC->CFGR & RCC_CFGR_SWS_PLL1))
  67.     {
  68.     };
  69. }

  70. /* Displays MCO on PC9 */
  71. static void InitializeMCO(void)
  72. {
  73.     RCC->CFGR |= RCC_CFGR_MCO2;
  74.     RCC->CFGR |= (15 << 25);   // Reducing the output so I can probe it with my slow USB scope

  75.     RCC->AHB4ENR &= ~RCC_AHB4ENR_GPIOCEN;
  76.     RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN;

  77.     GPIOC->MODER &= ~GPIO_MODER_MODER9;
  78.     GPIOC->MODER |= GPIO_MODER_MODER9_1;

  79.     GPIOC->OTYPER &= ~GPIO_OTYPER_OT_9;
  80.     GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR9;

  81.     GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR9;
  82.     GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;

  83.     GPIOC->AFR[0] &= ~GPIO_AFRL_AFRL0;
  84. }

  85. static void InitializeDMA()
  86. {
  87.     RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN | RCC_AHB2ENR_D2SRAM3EN);   // Enable the SRAM
  88.     RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;   // DMA2 clock enable;

  89.     // Set the peripheral and memory addresses:
  90.     DMA2_Stream4->PAR = (uint32_t) &(SPI1->TXDR);
  91.     DMA2_Stream4->M0AR = (uint32_t ) &(aTxBuffer[0]);

  92.     DMA2_Stream4->CR = 0;
  93.     DMA2_Stream4->CR |= (1u << DMA_SxCR_DIR_Pos);   // Memory to peripheral
  94.     DMA2_Stream4->CR |= DMA_SxCR_MINC;   // Memory increment mode
  95.     DMA2_Stream4->CR |= (3u << DMA_SxCR_PL_Pos);   // Very high priority

  96.     DMA2_Stream4->NDTR = 0x12; //DMA transfer length

  97.     DMA2_Stream4->CR |= DMA_SxCR_EN; // Enable DMA stream

  98.     DMAMUX1_Channel12->CCR = 0x26;
  99. }

  100. static void InitializeMasterTxSPI(void)
  101. {
  102.     RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN;   // Enable usage of GPIOA

  103.     GPIOA->MODER &= ~GPIO_MODER_MODER5;
  104.     GPIOA->MODER |= GPIO_MODER_MODER5_1;   // Alternate function for SPI1 SCK on PA5
  105.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5;   // High Speed on PA5
  106.     GPIOA->AFR[0] |= (0x05 << 5 * 4);   // AFRL selected AF5 (SPI1 SCK) for PA5

  107.     GPIOA->MODER &= ~GPIO_MODER_MODER6;
  108.     GPIOA->MODER |= GPIO_MODER_MODER6_1;   // Alternate function for SPI1 MISO on PA6
  109.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6;   // High Speed on PA6
  110.     GPIOA->AFR[0] |= (0x05 << 6 * 4);   // AFRL selected AF5 (SPI1 MISO) for PA6

  111.     GPIOA->MODER &= ~GPIO_MODER_MODER7;
  112.     GPIOA->MODER |= GPIO_MODER_MODER7_1;   // Alternate function for SPI1 MOSI on PA7
  113.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7;   // High Speed on PA7
  114.     GPIOA->AFR[0] |= (0x05 << 7 * 4);   // AFRL selected AF5 (SPI1 MOSI) for PA7

  115.     GPIOA->MODER &= ~GPIO_MODER_MODER4;
  116.     GPIOA->MODER |= GPIO_MODER_MODER4_1;   // Alternate function for SPI1 NSS on PA4
  117.     GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4;   // High Speed on PA4
  118.     GPIOA->AFR[0] |= (0x05 << 4 * 4);   // AFRL selected AF5 (SPI1 NSS) for PA4

  119.     GPIOA->PUPDR |=  GPIO_PUPDR_PUPDR4;   // Ensure all pull up pull down resistors are enabled
  120.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5;   // Ensure all pull up pull down resistors are disabled
  121.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6;   // Ensure all pull up pull down resistors are disabled
  122.     GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7;   // Ensure all pull up pull down resistors are disabled

  123.     RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;

  124.     SPI1->CR1 = SPI_CR1_SSI;

  125.     SPI1->CFG1 = (2u << SPI_CFG1_MBR_Pos) |
  126.                      (7u << SPI_CFG1_CRCSIZE_Pos) |
  127.                      SPI_CFG1_TXDMAEN | // SPI_CFG1_RXDMAEN |
  128.                      (7u << SPI_CFG1_FTHLV_Pos) |
  129.                      (7u << SPI_CFG1_DSIZE_Pos);
  130.     SPI1->CFG2 = SPI_CFG2_SSM | SPI_CFG2_MASTER;

  131.     SPI1->CR1 |= SPI_CR1_SPE;
  132.     SPI1->CR1 |= SPI_CR1_CSTART;
  133. }
复制代码
hzp_bbs1
6楼-- · 2019-07-15 20:51
现在考虑所有事情,DMAMUX的基本功能并不太难。手册指出:
DMAMUX1通道0到7连接到DMA1通道0到7
DMAMUX1通道8至15连接到DMA2通道0至7
DMAMUX2通道0到7连接到BDMA通道0到7
这些以及多路复用器输入到资源表的分配是使DMA运行的关键(至少以与旧系列MCUS相同的方式运行)。例如,SPI1_TX位于DMAMUX1的第38个DMA请求MUX输入上(参见参考手册中的表110)。这意味着我可以使用DMA1或DMA2(而不是BDMA,因为它链接到DMAMUX2)。我可以选择我想要的任何流,他们只需要遵循以下规则:
DMA1_Stream_x - > DMAMUX1_Channel_x
DMA2_Stream_x - > DMAMUX1_Channel_(x + 8)
因此,这就是通过DMAMUX的特定通道将外设实际链接到DMA流的方式。
还有几点需要注意:
不要忘记设置SPI_CR1_CSTART位(这是H7的新功能)。
注意SPI->CR2寄存器。如果为其写入值,则SPI传输将在预定义的数据传输开始后停止。如果我设置了CR2,那么无限循环(如我的示例所示)将无法工作(我们只会获得一次完整的SPI传输)。
虽然现在整个事情对我来说似乎有点明显,但我仍觉得参考文献中的信息有些缺乏。旧系列手册中的DMA操作稍微好一点。例如,我仍然不知道如何(以及何时)利用DMAMUX的剩余功能(如请求生成器和诸如此类)。此外,我不太确定如何实现内存到内存的传输。
我希望这有助于任何想深入研究ARM编程的人。

一周热门 更多>