如何在STM32F103C8T6上正确配置SPI外设?

编程语言 2026-07-09

我已经尝试用示波器监控MOSI引脚的输出,但没有任何输出。我按照初始化时钟、禁用并重置SPI → CR1寄存器、设置GPIO,然后在启用SPI接口之前配置CR1寄存器的流程来做,但在运行测试时:

uint8_t buff[3] = {0x66, 0x11, 0x35}
spi1_transmit_blocking(buff, buff_size);

在通过调试模式查看时,SPI的 DR寄存器里总是显示0xff。

/*
 * spi.c
 *
 *  Created on: May 21, 2026
 *      Author: hillary
 */


#include "spi.h"

void spi1_init(void)
{
    RCC->APB2ENR |= (RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN);
    RCC->AHBENR  |= RCC_AHBENR_DMA1EN;
    __DSB();

    // Disable SPI1
    //SPI1->CR1 |= SPI_CR1_SPE;

    // reset cr1 register
    SPI1->CR1 = 0x0000;

    /* Setup SPI1 Pins */
    // PA5 = SPI1 SCK
    // PA7 = SPI_MOSI
    GPIOA->CRL &= ~(GPIO_CRL_MODE5 | GPIO_CRL_CNF5);
    GPIOA->CRL |= (GPIO_CRL_MODE5 | GPIO_CRL_CNF5_1);

    GPIOA->CRL &= ~(GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
    GPIOA->CRL |= (GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7);

    /** Initialise SPI1 CR1 Register **/
    /* Master Mode, Software Slave Management, prescaler /4 */
    SPI1->CR1 |= (SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE);
    SPI1->CR1 |= SPI_CR1_BR_0;
    SPI1->CR1 |= SPI_CR1_SPE;
}

void spi1_transmit_blocking(const uint8_t *buf, uint16_t len)
{

    if((SPI1->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE){
        SPI1->CR1 |= SPI_CR1_SPE;
    }

    for (uint16_t i = 0; i < len; i++) {
        while (!(SPI1->SR & SPI_SR_TXE));  // wait TX empty
        SPI1->DR = buf[i];
    }

    __IO uint32_t tmpreg_ovr = 0x00U;
    tmpreg_ovr = SPI1->DR;
    tmpreg_ovr = SPI1->SR;
    (void)tmpreg_ovr;
}

解决方案

RCC->APB2ENR |= (RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPAEN);

要启用SPI1,除了要启用 RCC_APB2ENR_SPI1EN 位外,还需要启用 RCC_APB2ENR_AFIOEN 位。

/* enable GPIOA and SPI1 clock */
RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_AFIOEN);

顺便说一句,你的代码

// Disable SPI1
SPI1->CR1 |= SPI_CR1_SPE;

并不是像注释中所说的那样禁用SPI1端口,实际上是启用了SPI1。要禁用SPI1,应该是

// Disable SPI1
SPI1->CR1 &= ~SPI_CR1_SPE;

但由于你接下来的代码把整个 SPI1-CR1 寄存器重置为

SPI1->CR1 = 0x0000;

因此上面这两行是多余且不必要的。

更新

根据你的评论,与等待更多信息不同,我测试了以下配置代码,其本质与您的代码基本相同:

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;    // enable GPIOA clock

// reset PA4, PA5, PA7 MODE and CNF to 0b00
GPIOA->CRL &= ~(GPIO_CRL_MODE4 | GPIO_CRL_CNF4 |
                GPIO_CRL_MODE5 | GPIO_CRL_CNF5 |
                GPIO_CRL_MODE6 | GPIO_CRL_CNF6 |
                GPIO_CRL_MODE7 | GPIO_CRL_CNF7);

// init PA5 SCK, PA7 MOSI - Mode = b11 (50Mhz) - CNF = b10 (Alt Function PP)
GPIOA->CRL |= (GPIO_CRL_MODE5 | GPIO_CRL_CNF5_1) |
              (GPIO_CRL_MODE7 | GPIO_CRL_CNF7_1);

// init PA6 MISO - Mode = b00 (Input) - CNF = b01 (Floating)
// GPIOA->CRL &= ~GPIO_CRL_MODE6;
// GPIOA->CRL &= ~GPIO_CRL_CNF6_1;
// GPIOA->CRL |= GPIO_CRL_CNF6_0;

// init PA4 NSS - Mode = b11 (50MHz) - CNF = b00 (General Output PP)
GPIOA->CRL |= GPIO_CRL_MODE4;

RCC->APB2ENR |= (RCC_APB2ENR_SPI1EN | RCC_APB2ENR_AFIOEN);    // enable SPI1 clock
SPI1->CR1  = SPI_CR1_MSTR |                // SPI master mode
             SPI_CR1_BR_0;                 // bit rate prescale / 4 (72MHz/4 = 18MHz)

SPI1->CR1 &= ~(SPI_CR1_DFF |               // 8-bit
               SPI_CR1_LSBFIRST |          // MSB First
               SPI_CR1_RXONLY);            // Full duplex

SPI1->CR1 |= (SPI_CR1_SSM |                // Software Slave Management enabled
              SPI_CR1_SSI);                // Slave Select Internal

以下配置代码,其本质与您的代码基本相同:我将如何测试代码如下:

uint8_t tx_data[] = "Hello World\n";
const uint16_t len = sizeof(tx_data)/sizeof(tx_data[0]) - 1;

SPI1->CR1 |= SPI_CR1_SPE;              // Enable SPI1
GPIOA->BRR = GPIO_BRR_BR4;             // CS activated (PA4 = LOW)
for (int i = 0; i<len; i++) {
    while (!(SPI1->SR & SPI_SR_TXE));  // wait till tx buffer is empty
    SPI1->DR = tx_data[i];             // load a byte to DR register

//    while (!(SPI1->SR & SPI_SR_RXNE)); // wait till rx buffer is filled
//    rx_data[i] = SPI1->DR;             // read a byte from DR register

}
while (SPI1->SR & SPI_SR_BSY);
GPIOA->BSRR = GPIO_BSRR_BS4;           // CS de-activated (PA4 = HIGH)
SPI1->CR1 &= ~SPI_CR1_SPE;             // Disable SPI1

测试 SPI 在 STM32F103CB 上

一切按预期工作,如果你的代码没有按预期工作,极有可能是因为你未展示的那段代码所致。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章