Problemas del receptor en el SPI basado en interrupciones STM32H7

3

Quiero configurar un transmisor / receptor esclavo SPI basado en interrupciones simple en una MCU STM32H7. Aunque tengo bastante experiencia con las series más antiguas de MCU ARM STM32, parece que muchas cosas son different para la serie H7 y requiere un gran esfuerzo para volver a aprender y remasterizar incluso algunas de las características más comunes.

Quiero ejecutar un ejemplo simple donde envío 8 bytes desde la PC (lado maestro) y recibo 8 bytes de la MCU ARM (lado esclavo). Estoy usando un cable MPSSE C232HM para enviar / recibir datos desde la PC.

El código MCU SPI Tx / Rx se presenta a continuación:

#include "stm32h7xx.h"

static void InitializeMCO(void);
static void ConfigureHSI(void);
static void InitializeMasterTxSPI(void);

volatile uint8_t aTxBuffer[8] = {'S','T','M','3','2','O','u','t'};
volatile uint8_t aRxBuffer[8] = {'E','m','p','t','y','A','r','r'};

uint32_t aRxBuffPos;
uint32_t aTxBuffPos;

uint8_t rxCounter;
uint8_t txCounter;

void SPI1_IRQHandler(void);

int main()
{
        aRxBuffPos = 0;
        aTxBuffPos = 0;

        rxCounter = 0;
        txCounter = 0;

    ConfigureHSI();
    InitializeMCO();
        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) | 
                        ((50u - 1u) << RCC_PLL1DIVR_N1_Pos)
            ;

    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->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_MODER15;
    GPIOA->MODER |= GPIO_MODER_MODER15_1;   // Alternate function for SPI1 NSS on PA4
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15;   // High Speed on PA4
    GPIOA->AFR[1] |= (0x05 << (15 - 8) * 4);   // AFRL selected AF5 (SPI1 NSS) for PA4

    GPIOA->PUPDR |=  GPIO_PUPDR_PUPDR15;   // 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->CFG1 = (0u << SPI_CFG1_FTHLV_Pos) | 
                     (7u << SPI_CFG1_DSIZE_Pos);        
        SPI1->CFG2 = 0;

        //SPI1->CFG2 |= SPI_CFG2_LSBFRST;
        //SPI1->CFG2 |= SPI_CFG2_CPHA;
        //SPI1->CFG2 |= SPI_CFG2_CPOL;
        //SPI1->CR2 = 8;

    NVIC_SetPriority(SPI1_IRQn, 1);
    NVIC_EnableIRQ(SPI1_IRQn);

        //SPI1->IER |= SPI_IER_DXPIE;
        SPI1->IER |= SPI_IER_RXPIE;
        SPI1->IER |= SPI_IER_TXPIE;

        SPI1->CR1 |= SPI_CR1_SPE;
}

void SPI1_IRQHandler(void)
{  
  if(SPI1->SR & SPI_SR_RXP)
  {
   //while(SPI1->SR & SPI_SR_RXP)
   {
      aRxBuffer[aRxBuffPos++] = *((__IO uint8_t *)&SPI1->RXDR);
     //aRxBuffer[aRxBuffPos++] = *(volatile uint8_t *) SPI1->RXDR; 
     //aRxBuffer[aRxBuffPos++] = SPI1->RXDR;
   }
  }

  if(SPI1->SR & SPI_SR_TXP)
  {
   //while(SPI1->SR & SPI_SR_TXP)
   {
     *(volatile uint8_t *) &(SPI1)->TXDR = aTxBuffer[aTxBuffPos++];
     //*(volatile uint8_t *) &(SPI1)->TXDR = RxBuff[SPI_ByteCount++]; 
   }
  }

  if (aTxBuffPos >= 8)
  {
    aTxBuffPos = 0;
    txCounter++;
  }

  if (aRxBuffPos >= 8)
  {
    aRxBuffPos = 0;
    rxCounter++;
  }
}

El código se compiló utilizando IAR Embedded Workbench.

El código PC SPI Tx / Rx se presenta a continuación:

#include <stdio.h>
#include <Windows.h>
#include "libMPSSE_spi.h"

void print_and_quit(char cstring[]) {
    printf("%s\n", cstring);
    system("pause");
    exit(1);
}

int main(int argc, char **argv) {

    Init_libMPSSE();

    FT_STATUS status;
    FT_DEVICE_LIST_INFO_NODE channelInfo;
    FT_HANDLE handle;

    // check how many MPSSE channels are available
    uint32 channelCount = 0;
    status = SPI_GetNumChannels(&channelCount);
    if (status != FT_OK)
        print_and_quit("Error while checking the number of available MPSSE channels.");
    else if (channelCount < 1)
        print_and_quit("Error: no MPSSE channels are available.");

    printf("There are %d channels available.\n\n", channelCount);

    for (int i = 0; i < channelCount; i++) {
        status = SPI_GetChannelInfo(i, &channelInfo);
        if (status != FT_OK)
            print_and_quit("Error while getting details for an MPSSE channel.");

        printf("Channel number: %d\n", i);
        printf("Description: %s\n", channelInfo.Description);
        printf("Serial Number: %d\n", channelInfo.SerialNumber);
    }

    // ask the user to select a channel
    uint32 channel = 0;
    //printf("\nEnter a channel number to use: ");
    //scanf_s("%d", &channel);

    // open the MPSSE channel (get the handle for it)
    status = SPI_OpenChannel(channel, &handle);
    if (status != FT_OK)
        print_and_quit("Error while opening the MPSSE channel.");
    else
        printf("Channel opened\n");

    ChannelConfig channelConfig;
    channelConfig.ClockRate = 4000000;
    channelConfig.configOptions = SPI_CONFIG_OPTION_MODE0 | SPI_CONFIG_OPTION_CS_DBUS3 | SPI_CONFIG_OPTION_CS_ACTIVELOW;
    channelConfig.LatencyTimer = 1;
    status = SPI_InitChannel(handle, &channelConfig);
    if (status != FT_OK)
        print_and_quit("Error while initializing the MPSSE channel.");
    else
        printf("Channel initialized\n");

    uint8 tx_buffer[8] = { 'P' ,  'C',  'S',  'P',  'I',  'O',  'u', 't', },
          rx_buffer[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

    uint32 transferCount = 0;
    uint32 options = SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES | SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE | SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE;

    //while (1)
    {
        status = SPI_ReadWrite(handle, rx_buffer, tx_buffer, 8, &transferCount, options);
        printf("tx = %c %c %c %c %c %c %c %c, rx = %c %c %c %c %c %c %c %c\n", tx_buffer[0], tx_buffer[1], tx_buffer[2], tx_buffer[3], tx_buffer[4], tx_buffer[5], tx_buffer[6], tx_buffer[7],
                                                                               rx_buffer[0], rx_buffer[1], rx_buffer[2], rx_buffer[3], rx_buffer[4], rx_buffer[5], rx_buffer[6], rx_buffer[7]);
        Sleep(500);
    }
    system("pause");

    Cleanup_libMPSSE();
    return 0;
}

El código se compiló con Microsoft Visual Studio.

Al principio, tenía la impresión de que todo funciona correctamente. Al menos desde el lado de la PC aparentemente pude enviar y recibir datos a / desde la MCU. Al realizar una depuración más cercana, noté que solo la línea de PC a MCU funciona correctamente. La MCU siempre recibe datos de todo cero.

Para ilustrar, esta es la salida del depurador antes de iniciar la transferencia:

Idealmente,elcontenidodeaRxBufferdebesobrescribirsedespuésdequelatransferenciahayacomenzado.Enrealidad,laMCUtransmitetodossusdatoscorrectamente,aunquerecibedatosdetodoceroenlugardeloquerealmenteseenvió:

He realizado varios intentos de solución de problemas como:

  • Probé las señales SCK / MISO / MOSI con un osciloscopio y todas parecen ser correctas, es decir, no hay problemas físicos con los cables o las pistas de PCB. En otras palabras, hay una señal digital MOSI adecuada que va del C232HM al MCU.
  • He jugado un poco con todas las combinaciones de fase / polaridad de reloj en ambos lados, MCU y PC, y ninguno de los ajustes parece generar datos en el lado del receptor (puedo corromper los datos recibidos en el lado de la PC, aunque es de esperar).
  • Curiosamente, si desconecto el cable MOSI de C232HM y conecto el pin MOSI en el lado de la MCU a un riel de + 3.3V, mi búfer aRxBuffer se llena con 0xFF. Así que parece que hay alguna respuesta receptiva en el lado de la MCU, aunque no se ejecuta en el borde CLK.

¿Alguien tiene una corazonada de dónde podría estar el problema?

    
pregunta K.R.

1 respuesta

0

Esta respuesta no es una solución, sino una sugerencia de un método para aislar el problema.

Supongo que tiene uno o más pines de salida STM32H7 que puede manejar como GPIO, y que puede detectar con un osciloscopio o analizador lógico.

Primero, determine lo que el STM32H7 reconoce en los pines de entrada SPI. En su código de fondo (sin interrupción), ejecute un bucle estrecho que lea los datos SPI y / o los pines del reloj (del registro de datos de entrada GPIO asociado), y escriba los bits en los pines de salida GPIO que pueda detectar un osciloscopio o analizador lógico.

Debería ver que los pines de salida GPIO siguen los datos SPI y las entradas de reloj, con un retraso debido al muestreo GPIO. Si no ve esto, sabe que hay algún problema con el muestreo del pin de entrada.

Para esto, querrás que la velocidad del reloj SPI sea mucho más lenta que la del microcontrolador, y que la salida GPIO esté configurada a una velocidad alta.

Segundo, asumiendo que la prueba anterior funciona, verifique que el SPI esté recibiendo las palabras de entrada correctamente. Para hacer esto, actualice su rutina de interrupción SPI para alternar una salida GPIO cada vez que se recibe un valor. Podría usar el bit de orden inferior del índice aRxBuffPos como el valor de salida de GPIO, debería verlo cambiar cada vez que se recibe una palabra.

Tercero, asumiendo que todas las pruebas anteriores funcionan, verifique los datos. Su código sobrescribe el búfer de datos de recepción en cada 8 valores, pero desea ver si alguna vez recibe un valor distinto de cero. Por lo tanto, modifique el código de interrupción para impulsar el pin GPIO alto cuando vea un valor distinto de cero. Si alguna vez ve que la línea GPIO se eleva, puede recibir un valor distinto de cero. Luego actualizaría la prueba para escribir el bit de orden inferior del valor de los datos recibidos en la línea GPIO y ver qué tiene que escribir en el lado del host (PC) para obtener datos que no sean cero recibidos por el STM32H7.

Creo que esta secuencia está bastante garantizada para aislar el problema.

    
respondido por el D. Brown

Lea otras preguntas en las etiquetas

Comentarios Recientes

también se sospechan en los procesadores lanzados después de noviembre de 2008. Aunque tradicionalmente han tenido prioridad sobre los dispositivos flash PCI de SPDIF (interfaz periférica en serie), su presencia es generalmente un error general asociado con dispositivos más rápidos Roscas Busbix / CKUSB8p50SP / CKUSB8p8SC - Incluye un documento sobre "Error o daño de sensores hoy en unidades flash SPI basadas en bus" - MSDN; Mayo de 2010. Disponible para I + D interesado en probar esta información a la comunidad... Lees verder