Recientemente cambié a los MCU SM32H7 recién producidos y actualmente estoy en el proceso de migrar parte de mi código antiguo a ellos (quiero ver cómo manejan algunas aplicaciones que requieren un poco más de velocidad). Los H7 cuentan con un multiplexor de solicitud de DMA, que estaba ausente en los F7, F4 o F3 más antiguos con los que solía trabajar. En los últimos modelos, el mapeo de canales DMA se logró ingresando un valor correcto en el registro de control DMA_x Stream_y. Por ejemplo
DMA2_Stream3->CR |= (0x3 << DMA_SxCR_CHSEL_Pos);
seleccionaría el tercer canal para DM2 Stream3 (en el caso de MCU F7, esto correspondería a la solicitud SPI1 TX DMA: según tengo entendido, el mapeo de canal DMA a DMA ya no está "cableado" y puede ser reconfigurado manualmente en la serie H7. Como dice el manual, DMAMux1 debe usarse para enrutar una línea de solicitud de DMA al canal DMA . Desafortunadamente, la configuración de DMAMUX se describe bastante mal en el manual de referencia. No he logrado comprender cómo exactamente un flujo DMA, un periférico y un canal DMA están interconectados a través del multiplexor.Abajo hay un fragmento de código que debería, idealmente,
Habilite la transmisión DMA para una transferencia SPI TX.
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; // Enable usage of GPIOA
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
GPIOA->MODER &= ~GPIO_MODER_MODER5;
GPIOA->MODER |= GPIO_MODER_MODER5_1; // Alternate function for SPI1 SCK on PA5
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; // High Speed on PA5
GPIOA->AFR[0] |= (0x05 << 5 * 4); // AFRL selected AF5 (SPI1 SCK) for PA5
GPIOA->MODER &= ~GPIO_MODER_MODER6;
GPIOA->MODER |= GPIO_MODER_MODER6_1; // Alternate function for SPI1 MISO on PA6
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6; // High Speed on PA6
GPIOA->AFR[0] |= (0x05 << 6 * 4); // AFRL selected AF5 (SPI1 MISO) for PA6
GPIOA->MODER &= ~GPIO_MODER_MODER7;
GPIOA->MODER |= GPIO_MODER_MODER7_1; // Alternate function for SPI1 MOSI on PA7
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7; // High Speed on PA7
GPIOA->AFR[0] |= (0x05 << 7 * 4); // AFRL selected AF5 (SPI1 MOSI) for PA7
GPIOA->MODER &= ~GPIO_MODER_MODER4;
GPIOA->MODER |= GPIO_MODER_MODER4_1; // Alternate function for SPI1 NSS on PA7
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4; // High Speed on PA7
GPIOA->AFR[0] |= (0x05 << 4 * 4); // AFRL selected AF5 (SPI1 NSS) for PA7
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR4_0; // Ensure all pull up pull down resistors are enabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7; // Ensure all pull up pull down resistors are disabled
SPI1->CFG1 = (1u << SPI_CFG1_MBR_Pos) | // Master baud rate: master clock / 2
(7u << SPI_CFG1_CRCSIZE_Pos) | // Length of CRC frame
SPI_CFG1_TXDMAEN | SPI_CFG1_RXDMAEN | // Enable RX/TX DMA
(7u << SPI_CFG1_FTHLV_Pos) | // FIFO threshold level
(7u << SPI_CFG1_DSIZE_Pos) //Number of bits in at single SPI data frame
;
SPI1->CFG2 = SPI_CFG2_SSOE | // SS output enable
SPI_CFG2_MASTER // SPI Master
;
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // DMA2 clock enable;
DMA2_Stream3->CR = 0u;
DMA2_Stream3->PAR = (uint32_t) &(SPI1->TXDR);
DMA2_Stream3->M0AR = (uint32_t) &(Data_Buffer[0]);
DMA2_Stream3->CR |= (1u << DMA_SxCR_DIR_Pos);
DMA2_Stream3->CR |= DMA_SxCR_MINC;
DMA2_Stream3->CR |= DMA_SxCR_PL;
DMA2_Stream3->NDTR = 1000;
// 5. Use DMAMux1 to route a DMA request line to the DMA channel.
DMAMUX1_Channel0->CCR = (37u << DMAMUX_CxCR_DMAREQ_ID_Pos);
SPI1->CR1 |= SPI_CR1_SPE;
DMA2_Stream3->CR |= DMA_SxCR_EN;
Este código se compila y puedo cargarlo en un MCU STM32H753ZIT6. El código completo también tiene configuración de PLL, que no está incluida en el fragmento anterior (la inicialización de PLL funciona bien, ya que puedo sondear el reloj del sistema de 400 MHz en el pin MCO).
La configuración del multiplexor DMA está, obviamente, incompleta. Solo he seleccionado una asignación adecuada (al menos creo que es adecuada) de entradas de multiplexor a recursos. Ni siquiera estoy seguro de si el canal del multiplexor es correcto (o si su elección es arbitraria en las MCU H7).
Entonces, mi pregunta es: ¿cuál sería la forma correcta de configurar el multiplexor DMA para la transferencia SPI TX? Si puedo ejecutar este MWE, seré más o menos capaz de terminar de migrar el resto de mi código.
Gracias de antemano.
ACTUALIZAR:
Entonces, he estado tratando de seguir el consejo del usuario 9403409, pero, desafortunadamente, no pude ir muy lejos. Todavía no puedo hacer que SPI funcione sobre DMA en los microcontroladores de la serie H7. Ahora puedo hacer que SPI funcione sin DMA en el H7:
#include "stm32h7xx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void InitializeMCO(void);
static void ConfigureHSI(void);
static void InitializeMasterTxSPI(void);
uint8_t s_TransferBuffer[10];
int main()
{
s_TransferBuffer[0] = 0xAA;
s_TransferBuffer[1] = 0xBB;
s_TransferBuffer[2] = 0xCC;
ConfigureHSI();
InitializeMCO();
InitializeMasterTxSPI();
while(1){};
}
static void ConfigureHSI(void)
{
PWR->CR3 |= PWR_CR3_SCUEN;
PWR->D3CR |= (PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0);
while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY)
{
};
FLASH->ACR = FLASH_ACR_LATENCY_2WS;
RCC->CR |= RCC_CR_HSION;
while ((RCC->CR & RCC_CR_HSIRDY) != RCC_CR_HSIRDY)
{
};
RCC->PLLCKSELR = (4u << RCC_PLLCKSELR_DIVM1_Pos) |
(32u << RCC_PLLCKSELR_DIVM2_Pos) |
(32u << RCC_PLLCKSELR_DIVM3_Pos) |
RCC_PLLCKSELR_PLLSRC_HSI
;
RCC->PLLCFGR = RCC_PLLCFGR_DIVR1EN |
RCC_PLLCFGR_DIVQ1EN |
RCC_PLLCFGR_DIVP1EN |
(2u << RCC_PLLCFGR_PLL1RGE_Pos) |
(1u << RCC_PLLCFGR_PLL1VCOSEL_Pos)
;
RCC->PLL1DIVR = ((2u - 1u) << RCC_PLL1DIVR_R1_Pos) |
((2u - 1u) << RCC_PLL1DIVR_Q1_Pos) |
((2u - 1u) << RCC_PLL1DIVR_P1_Pos) |
((10u - 1u) << RCC_PLL1DIVR_N1_Pos) // Reducing the clock rate so I can probe it with my slow USB scope
;
RCC->D1CFGR = RCC_D1CFGR_D1CPRE_DIV1;
RCC->D1CFGR = RCC_D1CFGR_HPRE_DIV2 |
RCC_D1CFGR_D1PPRE_DIV2;
RCC->D2CFGR = RCC_D2CFGR_D2PPRE1_DIV2 |
RCC_D2CFGR_D2PPRE2_DIV2;
RCC->D3CFGR = RCC_D3CFGR_D3PPRE_DIV2;
RCC->CR |= RCC_CR_PLL1ON;
while (!(RCC->CR & RCC_CR_PLLRDY))
{
};
RCC->CFGR |= (1u << 25);
RCC->CFGR |= RCC_CFGR_SW_PLL1;
while (!(RCC->CFGR & RCC_CFGR_SWS_PLL1))
{
};
}
/* Displays MCO on PC9 */
static void InitializeMCO(void)
{
RCC->CFGR |= RCC_CFGR_MCO2;
RCC->CFGR |= (15 << 25); // Reducing the output so I can probe it with my slow USB scope
RCC->AHB4ENR &= ~RCC_AHB4ENR_GPIOCEN;
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN;
GPIOC->MODER &= ~GPIO_MODER_MODER9;
GPIOC->MODER |= GPIO_MODER_MODER9_1;
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_9;
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR9;
GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR9;
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;
GPIOC->AFR[0] &= ~GPIO_AFRL_AFRL0;
}
static void InitializeMasterTxSPI(void)
{
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; // Enable usage of GPIOA
GPIOA->MODER &= ~GPIO_MODER_MODER5;
GPIOA->MODER |= GPIO_MODER_MODER5_1; // Alternate function for SPI1 SCK on PA5
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; // High Speed on PA5
GPIOA->AFR[0] |= (0x05 << 5 * 4); // AFRL selected AF5 (SPI1 SCK) for PA5
GPIOA->MODER &= ~GPIO_MODER_MODER6;
GPIOA->MODER |= GPIO_MODER_MODER6_1; // Alternate function for SPI1 MISO on PA6
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6; // High Speed on PA6
GPIOA->AFR[0] |= (0x05 << 6 * 4); // AFRL selected AF5 (SPI1 MISO) for PA6
GPIOA->MODER &= ~GPIO_MODER_MODER7;
GPIOA->MODER |= GPIO_MODER_MODER7_1; // Alternate function for SPI1 MOSI on PA7
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7; // High Speed on PA7
GPIOA->AFR[0] |= (0x05 << 7 * 4); // AFRL selected AF5 (SPI1 MOSI) for PA7
GPIOA->MODER &= ~GPIO_MODER_MODER4;
GPIOA->MODER |= GPIO_MODER_MODER4_1; // Alternate function for SPI1 NSS on PA7
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4; // High Speed on PA7
GPIOA->AFR[0] |= (0x05 << 4 * 4); // AFRL selected AF5 (SPI1 NSS) for PA7
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR4; // Ensure all pull up pull down resistors are enabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7; // Ensure all pull up pull down resistors are disabled
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
SPI1->CR1 = 0;
SPI1->CFG1 = (3u << SPI_CFG1_MBR_Pos) |
(7u << SPI_CFG1_CRCSIZE_Pos) |
//SPI_CFG1_TXDMAEN | // SPI_CFG1_RXDMAEN |
(7u << SPI_CFG1_FTHLV_Pos) |
(7u << SPI_CFG1_DSIZE_Pos)
;
SPI1->CFG2 = SPI_CFG2_SSOE |
SPI_CFG2_MASTER
;
SPI1->CR2 |= 3;
SPI1->CR1 |= SPI_CR1_SPE;
SPI1->CR1 |= SPI_CR1_CSTART;
for (uint32_t i=0; i<3; i++)
{
while ((SPI1->SR & SPI_SR_TXP) != SPI_SR_TXP){};
*((__IO uint32_t *)&SPI1->TXDR) = *((uint32_t *)&s_TransferBuffer[i]);
}
}
El código anterior esencialmente hace tres cosas:
ConfigureHSI
inicializa el reloj HSI (he reducido la velocidad del reloj para poder hacer algunas pruebas con un osciloscopio USB lento que actualmente tengo en mis manos).InitializeMCO
muestra la salida del reloj principal (solo para asegurarse de que el reloj está configurado correctamente).InitializeMasterTxSPI
configura el SPI y envía un mensaje de tres bytes.Definitivamente puedo ver que se envía ese mensaje en mi osciloscopio: la base de tiempo es 200 ns/div, como referencia.
Por otro lado, si trato de rehacer todo a través de DMA, no veo ningún resultado. Así es como se ve mi código SPI basado en DMA:
#include "stm32h7xx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void InitializeMCO(void);
static void ConfigureHSI(void);
static void InitializeDMA(void);
static void InitializeMasterTxSPI(void);
uint8_t s_TransferBuffer[10];
int main()
{
s_TransferBuffer[0] = 0xAA;
s_TransferBuffer[1] = 0xBB;
s_TransferBuffer[2] = 0xCC;
ConfigureHSI();
InitializeMCO();
InitializeDMA();
InitializeMasterTxSPI();
while(1){};
}
/* Initializes the MCU clock */
static void ConfigureHSI(void)
{
PWR->CR3 |= PWR_CR3_SCUEN;
PWR->D3CR |= (PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0);
while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY)
{
};
FLASH->ACR = FLASH_ACR_LATENCY_2WS;
RCC->CR |= RCC_CR_HSION;
while ((RCC->CR & RCC_CR_HSIRDY) != RCC_CR_HSIRDY)
{
};
RCC->PLLCKSELR = (4u << RCC_PLLCKSELR_DIVM1_Pos) |
(32u << RCC_PLLCKSELR_DIVM2_Pos) |
(32u << RCC_PLLCKSELR_DIVM3_Pos) |
RCC_PLLCKSELR_PLLSRC_HSI
;
RCC->PLLCFGR = RCC_PLLCFGR_DIVR1EN |
RCC_PLLCFGR_DIVQ1EN |
RCC_PLLCFGR_DIVP1EN |
(2u << RCC_PLLCFGR_PLL1RGE_Pos) |
(1u << RCC_PLLCFGR_PLL1VCOSEL_Pos)
;
RCC->PLL1DIVR = ((2u - 1u) << RCC_PLL1DIVR_R1_Pos) |
((2u - 1u) << RCC_PLL1DIVR_Q1_Pos) |
((2u - 1u) << RCC_PLL1DIVR_P1_Pos) |
((10u - 1u) << RCC_PLL1DIVR_N1_Pos) // Reducing the clock rate so I can probe it with my slow USB scope
;
RCC->D1CFGR = RCC_D1CFGR_D1CPRE_DIV1;
RCC->D1CFGR = RCC_D1CFGR_HPRE_DIV2 |
RCC_D1CFGR_D1PPRE_DIV2;
RCC->D2CFGR = RCC_D2CFGR_D2PPRE1_DIV2 |
RCC_D2CFGR_D2PPRE2_DIV2;
RCC->D3CFGR = RCC_D3CFGR_D3PPRE_DIV2;
RCC->CR |= RCC_CR_PLL1ON;
while (!(RCC->CR & RCC_CR_PLLRDY))
{
};
RCC->CFGR |= (1u << 25);
RCC->CFGR |= RCC_CFGR_SW_PLL1;
while (!(RCC->CFGR & RCC_CFGR_SWS_PLL1))
{
};
}
/* Displays MCO on PC9 */
static void InitializeMCO(void)
{
RCC->CFGR |= RCC_CFGR_MCO2;
RCC->CFGR |= (15 << 25); // Reducing the output so I can probe it with my slow USB scope
RCC->AHB4ENR &= ~RCC_AHB4ENR_GPIOCEN;
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN;
GPIOC->MODER &= ~GPIO_MODER_MODER9;
GPIOC->MODER |= GPIO_MODER_MODER9_1;
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_9;
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR9;
GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR9;
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;
GPIOC->AFR[0] &= ~GPIO_AFRL_AFRL0;
}
static void InitializeDMA()
{
RCC->AHB2ENR |= (0x7 << 29); // Enable the SRAM
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DMA1 clock enable;
// Set the peripheral and memory addresses:
DMA1_Stream0->PAR = *((__IO uint32_t *)&SPI1->TXDR);
DMA1_Stream0->M0AR = *((uint32_t *)&s_TransferBuffer[0]);
DMA1_Stream0->CR = 0u;
DMA1_Stream0->CR |= (1u << DMA_SxCR_DIR_Pos); // Memory to peripheral
DMA1_Stream0->CR |= DMA_SxCR_MINC; // Memory increment mode
DMA1_Stream0->CR |= (3u << DMA_SxCR_PL_Pos); // Very high priority
DMA1_Stream0->NDTR = 3; // Number of data
DMAMUX1_Channel0->CCR = (38u << DMAMUX_CxCR_DMAREQ_ID_Pos);
}
static void InitializeMasterTxSPI(void)
{
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; // Enable usage of GPIOA
GPIOA->MODER &= ~GPIO_MODER_MODER5;
GPIOA->MODER |= GPIO_MODER_MODER5_1; // Alternate function for SPI1 SCK on PA5
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; // High Speed on PA5
GPIOA->AFR[0] |= (0x05 << 5 * 4); // AFRL selected AF5 (SPI1 SCK) for PA5
GPIOA->MODER &= ~GPIO_MODER_MODER6;
GPIOA->MODER |= GPIO_MODER_MODER6_1; // Alternate function for SPI1 MISO on PA6
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6; // High Speed on PA6
GPIOA->AFR[0] |= (0x05 << 6 * 4); // AFRL selected AF5 (SPI1 MISO) for PA6
GPIOA->MODER &= ~GPIO_MODER_MODER7;
GPIOA->MODER |= GPIO_MODER_MODER7_1; // Alternate function for SPI1 MOSI on PA7
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7; // High Speed on PA7
GPIOA->AFR[0] |= (0x05 << 7 * 4); // AFRL selected AF5 (SPI1 MOSI) for PA7
GPIOA->MODER &= ~GPIO_MODER_MODER4;
GPIOA->MODER |= GPIO_MODER_MODER4_1; // Alternate function for SPI1 NSS on PA4
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4; // High Speed on PA4
GPIOA->AFR[0] |= (0x05 << 4 * 4); // AFRL selected AF5 (SPI1 NSS) for PA4
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR4; // Ensure all pull up pull down resistors are enabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7; // Ensure all pull up pull down resistors are disabled
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
SPI1->CR1 = 0;
SPI1->CFG1 = (3u << SPI_CFG1_MBR_Pos) |
(7u << SPI_CFG1_CRCSIZE_Pos) |
SPI_CFG1_TXDMAEN | // SPI_CFG1_RXDMAEN |
(7u << SPI_CFG1_FTHLV_Pos) |
(7u << SPI_CFG1_DSIZE_Pos)
;
SPI1->CFG2 = SPI_CFG2_SSOE |
SPI_CFG2_MASTER
;
//SPI1->CR2 |= 3;
SPI1->CR1 |= SPI_CR1_SPE;
SPI1->CR1 |= SPI_CR1_CSTART;
DMA1_Stream0->CR |= DMA_SxCR_EN;
}
Esencialmente, es lo mismo, las únicas diferencias son que hay una InitializeDMA
función y la transferencia DMA se inicia a través del DMA1_Stream0->CR |= DMA_SxCR_EN
comando (como fue el caso en la serie anterior de MCU). Entonces, lamentablemente, todavía no puedo iniciar el SPI a través de DMA en los H7. Cualquier ayuda sería muy apreciada.
Entonces, en realidad logré hacer que SPI DMA se ejecutara. Publicando mi código de trabajo a continuación:
#include "stm32h7xx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void InitializeMCO(void);
static void ConfigureHSI(void);
static void InitializeDMA(void);
static void InitializeMasterTxSPI(void);
const uint8_t aTxBuffer[] = "Simple SPI message";
int main()
{
ConfigureHSI();
InitializeMCO();
InitializeDMA();
InitializeMasterTxSPI();
while (1)
{
/* Delay added to distinguish between the SPI messages: */
while(DMA2_Stream4->NDTR != 0) asm("nop");
for(uint32_t i=0; i<0xBF; i++) asm("nop");
//DMA2_Stream4->CR &= ~DMA_SxCR_EN;
DMA2->HIFCR |= DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4;
//DMA2_Stream4->PAR = (uint32_t) &(SPI1->TXDR);
DMA2_Stream4->M0AR = (uint32_t ) &(aTxBuffer[0]);
DMA2_Stream4->NDTR = 0x12;
DMA2_Stream4->CR |= DMA_SxCR_EN;
};
}
/* Initializes the MCU clock */
static void ConfigureHSI(void)
{
PWR->CR3 |= PWR_CR3_SCUEN;
PWR->D3CR |= (PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0);
while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY)
{
};
FLASH->ACR = FLASH_ACR_LATENCY_2WS;
RCC->CR |= RCC_CR_HSION;
while ((RCC->CR & RCC_CR_HSIRDY) != RCC_CR_HSIRDY)
{
};
RCC->PLLCKSELR = (4u << RCC_PLLCKSELR_DIVM1_Pos) |
(32u << RCC_PLLCKSELR_DIVM2_Pos) |
(32u << RCC_PLLCKSELR_DIVM3_Pos) |
RCC_PLLCKSELR_PLLSRC_HSI;
RCC->PLLCFGR = RCC_PLLCFGR_DIVR1EN |
RCC_PLLCFGR_DIVQ1EN |
RCC_PLLCFGR_DIVP1EN |
(2u << RCC_PLLCFGR_PLL1RGE_Pos) |
(1u << RCC_PLLCFGR_PLL1VCOSEL_Pos);
RCC->PLL1DIVR = ((2u - 1u) << RCC_PLL1DIVR_R1_Pos) |
((2u - 1u) << RCC_PLL1DIVR_Q1_Pos) |
((2u - 1u) << RCC_PLL1DIVR_P1_Pos) |
((10u - 1u) << RCC_PLL1DIVR_N1_Pos) // Reducing the clock rate so I can probe it with my slow USB scope
;
RCC->D1CFGR = RCC_D1CFGR_D1CPRE_DIV1;
RCC->D1CFGR = RCC_D1CFGR_HPRE_DIV2 | RCC_D1CFGR_D1PPRE_DIV2;
RCC->D2CFGR = RCC_D2CFGR_D2PPRE1_DIV2 | RCC_D2CFGR_D2PPRE2_DIV2;
RCC->D3CFGR = RCC_D3CFGR_D3PPRE_DIV2;
RCC->CR |= RCC_CR_PLL1ON;
while (!(RCC->CR & RCC_CR_PLLRDY))
{
};
RCC->CFGR |= (1u << 25);
RCC->CFGR |= RCC_CFGR_SW_PLL1;
while (!(RCC->CFGR & RCC_CFGR_SWS_PLL1))
{
};
}
/* Displays MCO on PC9 */
static void InitializeMCO(void)
{
RCC->CFGR |= RCC_CFGR_MCO2;
RCC->CFGR |= (15 << 25); // Reducing the output so I can probe it with my slow USB scope
RCC->AHB4ENR &= ~RCC_AHB4ENR_GPIOCEN;
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN;
GPIOC->MODER &= ~GPIO_MODER_MODER9;
GPIOC->MODER |= GPIO_MODER_MODER9_1;
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_9;
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR9;
GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR9;
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;
GPIOC->AFR[0] &= ~GPIO_AFRL_AFRL0;
}
static void InitializeDMA()
{
RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN | RCC_AHB2ENR_D2SRAM3EN); // Enable the SRAM
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // DMA2 clock enable;
// Set the peripheral and memory addresses:
DMA2_Stream4->PAR = (uint32_t) &(SPI1->TXDR);
DMA2_Stream4->M0AR = (uint32_t ) &(aTxBuffer[0]);
DMA2_Stream4->CR = 0;
DMA2_Stream4->CR |= (1u << DMA_SxCR_DIR_Pos); // Memory to peripheral
DMA2_Stream4->CR |= DMA_SxCR_MINC; // Memory increment mode
DMA2_Stream4->CR |= (3u << DMA_SxCR_PL_Pos); // Very high priority
DMA2_Stream4->NDTR = 0x12; //DMA transfer length
DMA2_Stream4->CR |= DMA_SxCR_EN; // Enable DMA stream
DMAMUX1_Channel12->CCR = 0x26;
}
static void InitializeMasterTxSPI(void)
{
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; // Enable usage of GPIOA
GPIOA->MODER &= ~GPIO_MODER_MODER5;
GPIOA->MODER |= GPIO_MODER_MODER5_1; // Alternate function for SPI1 SCK on PA5
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; // High Speed on PA5
GPIOA->AFR[0] |= (0x05 << 5 * 4); // AFRL selected AF5 (SPI1 SCK) for PA5
GPIOA->MODER &= ~GPIO_MODER_MODER6;
GPIOA->MODER |= GPIO_MODER_MODER6_1; // Alternate function for SPI1 MISO on PA6
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6; // High Speed on PA6
GPIOA->AFR[0] |= (0x05 << 6 * 4); // AFRL selected AF5 (SPI1 MISO) for PA6
GPIOA->MODER &= ~GPIO_MODER_MODER7;
GPIOA->MODER |= GPIO_MODER_MODER7_1; // Alternate function for SPI1 MOSI on PA7
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7; // High Speed on PA7
GPIOA->AFR[0] |= (0x05 << 7 * 4); // AFRL selected AF5 (SPI1 MOSI) for PA7
GPIOA->MODER &= ~GPIO_MODER_MODER4;
GPIOA->MODER |= GPIO_MODER_MODER4_1; // Alternate function for SPI1 NSS on PA4
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4; // High Speed on PA4
GPIOA->AFR[0] |= (0x05 << 4 * 4); // AFRL selected AF5 (SPI1 NSS) for PA4
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR4; // Ensure all pull up pull down resistors are enabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6; // Ensure all pull up pull down resistors are disabled
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7; // Ensure all pull up pull down resistors are disabled
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
SPI1->CR1 = SPI_CR1_SSI;
SPI1->CFG1 = (2u << SPI_CFG1_MBR_Pos) |
(7u << SPI_CFG1_CRCSIZE_Pos) |
SPI_CFG1_TXDMAEN | // SPI_CFG1_RXDMAEN |
(7u << SPI_CFG1_FTHLV_Pos) |
(7u << SPI_CFG1_DSIZE_Pos);
SPI1->CFG2 = SPI_CFG2_SSM | SPI_CFG2_MASTER;
SPI1->CR1 |= SPI_CR1_SPE;
SPI1->CR1 |= SPI_CR1_CSTART;
}
Ahora, la funcionalidad básica de DMAMUX no es demasiado difícil, considerando todo. El manual establece que:
Estos, junto con la asignación de entradas del multiplexor a las tablas de recursos, son las claves para que DMA funcione (al menos de la misma manera que en la serie anterior de MCUS). Por ejemplo, SPI1_TX está en la entrada MUX de solicitud de DMA número 38 de DMAMUX1 (consulte la tabla 110 en el manual de referencia). Esto significa que puedo emplear DMA1 o DMA2 (y no BDMA, ya que está vinculado a DMAMUX2). Puedo elegir cualquier transmisión que quiera, solo necesitan seguir la regla:
Entonces, así es como esencialmente vincula un periférico a una transmisión DMA a través de un canal particular de DMAMUX.
Un par de cosas a tener en cuenta también:
SPI_CR1_CSTART
bit (esto es algo nuevo para los H7).SPI->CR2
registro. Si le escribe un valor, la transferencia SPI se detendrá después de que haya comenzado el número predefinido de transferencias de datos. Un bucle infinito, como se presenta en mi ejemplo, no funcionará si se establece CR2 (solo obtendremos una única transferencia SPI completa).Si bien todo me parece un poco obvio ahora, aún diré que falta algo de información en la referencia. La operación DMA en los manuales de series anteriores se describía un poco mejor (al menos en mi opinión). Por ejemplo, todavía no sé cómo (y cuándo) utilizar la funcionalidad restante de DMAMUX (como los generadores de solicitudes y demás). Además, no estoy muy seguro de cómo se implementan las transferencias de memoria a memoria (probablemente tendré que aprender eso cuando llegue el momento adecuado).
Espero que esto ayude a cualquiera que quiera profundizar en la programación ARM.
Salud.
Su configuración de DMAMUX es correcta, solo necesita inicializar el registro DMAMUX1_CCR_DMAREQ_ID.
ACTUALIZACIÓN 1: he inicializado con éxito DMA para el temporizador TIM2:
// 5. Use DMAMux1 to route a DMA request line to the DMA channel. DMAMUX1_Channel0->CCR = (37u << DMAMUX_CxCR_DMAREQ_ID_Pos);
hace la línea
:?
karsten klein
CR