接收器在STM32H7基于中断的SPI上发出问题

2019-07-14 18:03发布

我想在STM32H7 MCU上配置一个简单的基于中断的SPI从发送器/接收器。尽管我对旧系列的STM32 ARM MCU有相当多的经验,但H7系列似乎有很多不同之处,需要花费很多精力来重新学习并重新设置一些更常见的功能。我想执行一个简单的例子,我从PC(主端)发送8个字节,从ARM MCU(从端)接收8个字节。我正在使用C232HM MPSSE电缆从PC发送/接收数据。MCU SPI Tx / Rx代码如下所示:
#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++;
  }
}
代码是使用IAR Embedded Workbench编译的。C SPI Tx / Rx代码如下所示:
  1. #include <stdio.h>
  2. #include <Windows.h>
  3. #include "libMPSSE_spi.h"

  4. void print_and_quit(char cstring[]) {
  5.     printf("%s ", cstring);
  6.     system("pause");
  7.     exit(1);
  8. }

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

  10.     Init_libMPSSE();

  11.     FT_STATUS status;
  12.     FT_DEVICE_LIST_INFO_NODE channelInfo;
  13.     FT_HANDLE handle;

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

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

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

  26.         printf("Channel number: %d ", i);
  27.         printf("Description: %s ", channelInfo.Description);
  28.         printf("Serial Number: %d ", channelInfo.SerialNumber);
  29.     }

  30.     // ask the user to select a channel
  31.     uint32 channel = 0;
  32.     //printf(" Enter a channel number to use: ");
  33.     //scanf_s("%d", &channel);

  34.     // open the MPSSE channel (get the handle for it)
  35.     status = SPI_OpenChannel(channel, &handle);
  36.     if (status != FT_OK)
  37.         print_and_quit("Error while opening the MPSSE channel.");
  38.     else
  39.         printf("Channel opened ");

  40.     ChannelConfig channelConfig;
  41.     channelConfig.ClockRate = 4000000;
  42.     channelConfig.configOptions = SPI_CONFIG_OPTION_MODE0 | SPI_CONFIG_OPTION_CS_DBUS3 | SPI_CONFIG_OPTION_CS_ACTIVELOW;
  43.     channelConfig.LatencyTimer = 1;
  44.     status = SPI_InitChannel(handle, &channelConfig);
  45.     if (status != FT_OK)
  46.         print_and_quit("Error while initializing the MPSSE channel.");
  47.     else
  48.         printf("Channel initialized ");

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

  51.     uint32 transferCount = 0;
  52.     uint32 options = SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES | SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE | SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE;

  53.     //while (1)
  54.     {
  55.         status = SPI_ReadWrite(handle, rx_buffer, tx_buffer, 8, &transferCount, options);
  56.         printf("tx = %c %c %c %c %c %c %c %c, rx = %c %c %c %c %c %c %c %c ", tx_buffer[0], tx_buffer[1], tx_buffer[2], tx_buffer[3], tx_buffer[4], tx_buffer[5], tx_buffer[6], tx_buffer[7],
  57.                                                                                rx_buffer[0], rx_buffer[1], rx_buffer[2], rx_buffer[3], rx_buffer[4], rx_buffer[5], rx_buffer[6], rx_buffer[7]);
  58.         Sleep(500);
  59.     }
  60.     system("pause");

  61.     Cleanup_libMPSSE();
  62.     return 0;
  63. }
复制代码代码是使用Microsoft Visual Studio编译的。一切正常。至少在PC方面我能够向MCU发送数据和从MCU接收数据,仔细调试后,我注意到只有PC到MCU的线路正常工作。MCU始终接收全零数据。这是启动传输之前的调试器输出: 1.png 理想情况下,传输开始后应覆盖aRxBuffer的内容。实际上,MCU正确地传输了所有数据,尽管它接收的是全零数据而不是实际发送的数据: 2.png
我做了各种故障排除尝试,例如:
  • 我用示波器探测了SCK / MISO / MOSI信号,它们看起来都是正确的,即导线或PCB走线没有物理问题。换句话说,有一个从C232HM到MCU的正确数字MOSI信号。
  • 我在MCU和PC端都使用了所有时钟相位/极性组合,并且没有任何调整似乎在接收器侧产生任何数据(我可以破坏PC端接收的数据,尽管这是预期)。
  • 如果我从C232HM断开MOSI电缆并将MCU侧的MOSI引脚连接到+ 3.3V电压轨,我的aRxBuffer缓冲区将填充0xFF。MCU侧存在一些接受响应,尽管它没有在CLK边缘执行。
有大神知道这是什么情况吗?


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
2条回答
皮皮鲁鲁鲁皮
1楼-- · 2019-07-15 00:34
好像不是这个原因,我在代码中添加了以下内容: if (aRxBuffPos >= 8) { aRxBuffPos = 0; for (uint8_t i=0; i<8; i++) aTxBuffer[i] = aRxBuffer[i]; rxCounter++; } 在前四个提示中,我得到了相同的结果(STM32OUT)。因此,Tx FIFO被缓冲掉,我从MCU返回空白数据(0x00)。在这种情况下,调试器并不是错误的原因。
zzpf
2楼-- · 2019-07-15 02:48
这个答案不是解决方案,而是建议一种隔离问题的方法。
我假设你有一个或多个STM32H7输出引脚, 你可以将其作为GPIO驱动,并且可以使用示波器或逻辑分析仪进行检测。
首先,确定STM32H7在SPI输入引脚上识别的内容。在后台(非中断)代码中,运行一个紧密循环,读取SPI数据和/或时钟引脚(来自相关的GPIO输入数据寄存器),并将这些位写入您可以感知的GPIO输出引脚示波器或逻辑分析仪。
你应该看到GPIO输出引脚遵循SPI数据和时钟输入,由于GPIO采样而延迟。如果没有看到这一点,就会知道输入引脚采样有问题。
为此,你需要SPI时钟速率比单片机时钟慢得多,并且GPIO输出设置为高速。
其次,假设上述测试有效,请检查SPI是否正确接收输入字。为此,请在每次接收到值时更新SPI中断例程以切换GPIO输出。可以使用aRxBuffPos索引的低位作为GPIO输出值,每次收到一个字时都应该看到它会发生变化。
第三,假设上述测试都有效,请检查数据。你的代码会覆盖每个8值的接收数据缓冲区,但希望查看是否收到非零值。因此,修改中断代码,以便在看到非零值时将GPIO引脚驱动为高电平。如果你看到GPIO线路变高,则可以获得非零值。然后更新测试,将接收到的数据值的低位写入GPIO线,看看必须在主机(PC)端写入什么才能获得STM32H7接收到的非零数据。

一周热门 更多>