我想在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代码如下所示:
- #include <stdio.h>
- #include <Windows.h>
- #include "libMPSSE_spi.h"
- void print_and_quit(char cstring[]) {
- printf("%s
", 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.
", 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
", i);
- printf("Description: %s
", channelInfo.Description);
- printf("Serial Number: %d
", channelInfo.SerialNumber);
- }
- // ask the user to select a channel
- uint32 channel = 0;
- //printf("
Enter 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
");
- 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
");
- 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
", 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;
- }
复制代码代码是使用Microsoft Visual Studio编译的。一切正常。至少在PC方面我能够向MCU发送数据和从MCU接收数据,仔细调试后,我注意到只有PC到MCU的线路正常工作。MCU始终接收全零数据。这是启动传输之前的调试器输出:
理想情况下,传输开始后应覆盖aRxBuffer的内容。实际上,MCU正确地传输了所有数据,尽管它接收的是全零数据而不是实际发送的数据:
我做了各种故障排除尝试,例如:- 我用示波器探测了SCK / MISO / MOSI信号,它们看起来都是正确的,即导线或PCB走线没有物理问题。换句话说,有一个从C232HM到MCU的正确数字MOSI信号。
- 我在MCU和PC端都使用了所有时钟相位/极性组合,并且没有任何调整似乎在接收器侧产生任何数据(我可以破坏PC端接收的数据,尽管这是预期)。
- 如果我从C232HM断开MOSI电缆并将MCU侧的MOSI引脚连接到+ 3.3V电压轨,我的aRxBuffer缓冲区将填充0xFF。MCU侧存在一些接受响应,尽管它没有在CLK边缘执行。
有大神知道这是什么情况吗?
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
我假设你有一个或多个STM32H7输出引脚, 你可以将其作为GPIO驱动,并且可以使用示波器或逻辑分析仪进行检测。
首先,确定STM32H7在SPI输入引脚上识别的内容。在后台(非中断)代码中,运行一个紧密循环,读取SPI数据和/或时钟引脚(来自相关的GPIO输入数据寄存器),并将这些位写入您可以感知的GPIO输出引脚示波器或逻辑分析仪。
你应该看到GPIO输出引脚遵循SPI数据和时钟输入,由于GPIO采样而延迟。如果没有看到这一点,就会知道输入引脚采样有问题。
为此,你需要SPI时钟速率比单片机时钟慢得多,并且GPIO输出设置为高速。
其次,假设上述测试有效,请检查SPI是否正确接收输入字。为此,请在每次接收到值时更新SPI中断例程以切换GPIO输出。可以使用aRxBuffPos索引的低位作为GPIO输出值,每次收到一个字时都应该看到它会发生变化。
第三,假设上述测试都有效,请检查数据。你的代码会覆盖每个8值的接收数据缓冲区,但希望查看是否收到非零值。因此,修改中断代码,以便在看到非零值时将GPIO引脚驱动为高电平。如果你看到GPIO线路变高,则可以获得非零值。然后更新测试,将接收到的数据值的低位写入GPIO线,看看必须在主机(PC)端写入什么才能获得STM32H7接收到的非零数据。
一周热门 更多>