STM32F407VGT6 上的 SPI 通信

问题描述 投票:0回答:2

我是 STM32 世界的新手,我正在尝试让我的 SPI 通信正常工作。我让它在主模式下运行并配置为软件从属管理模式。我使用的是单工通信。仅从主端传输的格式。

当我尝试将数据加载到 SPI 数据寄存器时,据我从调试器中得知,DR 没有任何变化。

任何帮助将不胜感激:

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define RCC_BASE_ADDR           0x40023800U
#define SPI_1_BASE_ADDR         0x40013000U
#define GPIO_A_BASE_ADDR        0x40020000U

// Register Address Offsets

    // RCC
#define RCC_CR_OFFSET           0x00U
#define RCC_AHB_1_EN_OFFSET     0x30U
#define RCC_APB_1_EN_OFFSET     0x40U
#define RCC_APB_2_EN_OFFSET     0x44U

    // SPI
#define SPIx_CR_1               0x00U
#define SPIx_CR_2               0x04U
#define SPIx_SR                 0x08U
#define SPIx_DR                 0x0CU
//#define SPIx_RX                   0x14U
//#define SPIx_TX                   0x18U
#define SPIx_CFGR               0x1CU

    // GPIO
#define MODE_R                  0x00U       // Mode of GPIO
#define OTYPE_R                 0x04U       // Output type of GPIO
#define OSPEED_R                0x08U       // Output speed
#define PUPD_R                  0x0CU       // input Config (Pull Up/Down)
#define IDR                     0x10U       // input Data reg
#define ODR                     0x14U       // Output Data reg
#define AFLR                    0x20U       // Alternate function GPIOx[0:7]
#define AFHR                    0x24U       // Alternate function GPIOx[8:15]


// Bit position offsets

// RCC
    //AHB1_EN
#define GPIO_A                  0

    // APB1_EN
#define SPI_1                   12

// SPI
    // CR1
#define BIDIMODE                15
#define BIDIOE                  14
#define DFF                     11
#define RXONLY                  10
#define SSM                     9
#define SSI                     8
#define LSBFIRST                7
#define SPE                     6
#define BAUD                    5       // [Width : 3]
#define MSTR                    2
#define CPOL                    1
#define CPHA                    0
    // CR2
#define TXE                     1       // Transfer buffer status
    // SR

// GPIOx


// Global vars

// ** RCC
// Enable register for APB2 buss
uint32_t* rcc_apb2_en = (uint32_t*) (RCC_BASE_ADDR + RCC_APB_2_EN_OFFSET);
uint32_t* rcc_ahb1_en = (uint32_t*) (RCC_BASE_ADDR + RCC_AHB_1_EN_OFFSET);

// ** GPIO
// GPIO Mode register
uint32_t* gpio_a_mode   = (uint32_t*) (GPIO_A_BASE_ADDR + MODE_R);
// GPIO Alternate function Selection register (LOW)
uint32_t* gpio_aflr     = (uint32_t*) (GPIO_A_BASE_ADDR + AFLR);

// ** SPI
// SPI Control Register 1
uint32_t* spi_cr1 = (uint32_t*) (SPI_1_BASE_ADDR + SPIx_CR_1);
// SPI Status register
uint32_t* spi_sr = (uint32_t*)  (SPI_1_BASE_ADDR + SPIx_SR);
// SPI data register
uint32_t* spi_dr = (uint32_t*)  (SPI_1_BASE_ADDR + SPIx_DR);



// Configure GPIO pins A5 (SCLK) and A7 (MOSI)
void GpioInit(){
    // Enable GPIO A on ahb1 bus
    *rcc_ahb1_en |= (0x1 << GPIO_A);

    // Set the mode for pins 5 and 6 on port A to their alternate functionality
    *gpio_a_mode |= (0x2 << 10) | (0x2 << 14);

    // Set Pin5 as Serial clock and pin7 as MOSI
    *gpio_aflr |= (0x5 << 20) | (0x5 << 28);
}

void SPI_init(){
    // Enable SPI on RCC clock
    *rcc_apb2_en |= (0x1 << SPI_1);

    // Reference to CR1 register in SPI 1 address region

    // Configure SPI as for 2 wire unidirectional mode aka full duplex transmission

    *spi_cr1 &= ~(0x1 << BIDIMODE);

    // Set Data frame as 16 bits wide
    *spi_cr1 |= (0x1 << DFF);

    // Not implemented

        // Internal slave select

        // Transfer is LSB first

        // Enable SPI

    // Enable software slave management
    *spi_cr1 |= (0x1 << SSM);

    // Set SSI to high to avoid mode fault
    *spi_cr1 |= (0x1 << SSI);
    // Set baud rate as default DIV2
    *spi_cr1 &= ~(0x7 << BAUD);

    // Configure as master
    *spi_cr1 |= (0x1 << MSTR);
    //  Set to mode 0
    *spi_cr1 &= ~(0x3 << CPHA);

}

void SPIx_write(uint8_t* value, uint8_t len){
    // Check the value of the DFF register
    uint8_t dff_val = ( (uint32_t)(*spi_cr1) >> DFF) & 0x1;
    // 16 Bit transmission
    if(dff_val){
        while(len > 0){
            // Check the status of the TX register
            // If empty, then transfer 2 bytes
            if(1 /*( (*spi_sr)  >> TXE ) & 0x1 */){
                // transfer first 2 bytes
//              *spi_dr = *((uint16_t*) value);
                *spi_dr = *((uint16_t*) value);
                // decrement by 2
                len-=2;
                // Move to next 2 bytes
                (uint16_t*)value++;
                continue;
            }
            // If not empty then delay
            for(uint8_t i = 0; i < 2; i++);
        }
    }
    // 8 Bit transmission
    else{
        while(len){
            // Check the status of the TX register
            // If empty, then transfer 1 byte
            if( (*spi_sr) & (0x1 << TXE) ){
                // transfer first byte
                *spi_dr = *value;
                // decrement by 1
                len--;
                // Move to next byte
                value++;
                continue;
            }
            // If not empty then delay
            for(uint8_t i = 0; i < 2; i++);
        }
    }

}


int main(void)
{
    char data[] = "hello";
    GpioInit();     // Initialize GPIO pins 5 & 7
    SPI_init();         // SPI instantiate
    // Enable the SPI for communication
    *spi_cr1 |= (0x1 << SPE);
    // Write to SPI data reg
    SPIx_write((uint8_t*)data, strlen(data));
    /* Loop forever */
    for(;;);
}
stm32 spi data-transfer stm32f4discovery
2个回答
1
投票
  1. 避免重新发明轮子并使用 STM 提供的 CMSIS 定义。你的尝试很天真。例如:
uint32_t* rcc_ahb1_en = (uint32_t*) (RCC_BASE_ADDR + RCC_AHB_1_EN_OFFSET);

会无缘无故地消耗 RAM 并添加另一个间接级别。此外,您需要使用一些

volatile
关键字让编译器知道某些内存位置可能会发生变化,而编译器操作不会对其进行任何可见的更改(它们被称为“容易产生副作用”)。

如果你想坚持自己的定义(祝你好运定义 10000 个寄存器),最好是:

#define rcc_ahb1_en ((volatile uint32_t*) (RCC_BASE_ADDR + RCC_AHB_1_EN_OFFSET))

和其他人一样


0
投票

您给出的问题的唯一症状如下:

当我尝试将数据加载到 SPI 数据寄存器时,据我从调试器中得知,DR 没有任何变化。

你误解了数据寄存器的工作原理。来自 参考手册 第 28.5.4 节:

DR[15:0]:数据寄存器 接收或传输的数据。 数据寄存器分为 2 个缓冲区 - 一个用于写入(发送缓冲区),另一个用于读取(接收缓冲区)。写入数据寄存器将写入 Tx 缓冲区,从数据寄存器读取将返回 Rx 缓冲区中保存的值。

由于您在单工模式下运行(仅传输),接收缓冲区中永远不会有任何内容,因此它始终读取为零。这包括调试器读取数据寄存器。

你可能还有其他问题,但你没有描述。

© www.soinside.com 2019 - 2024. All rights reserved.