如何在STM32F103C8T6上正确配置SPI外设?
我已经尝试用示波器监控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
一切按预期工作,如果你的代码没有按预期工作,极有可能是因为你未展示的那段代码所致。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。
