atmega328 上的 SPI 不工作

Posted

技术标签:

【中文标题】atmega328 上的 SPI 不工作【英文标题】:SPI on atmega328 not working 【发布时间】:2017-04-04 07:41:38 【问题描述】:

我们正在尝试使用 SPI 与另一个使用 atmel atmega328 MCU 的 IC 通信。目标 IC 接收命令(代码中的标头),然后将存储在请求寄存器中的信息反馈给我们(如果命令是写入,则将其写入)。但是,我们在这里遇到了两个问题:

    SPI 没有任何变化,唯一的变化是在 CS 线上(由我们控制)。没有 SPI 时钟,数据线上没有数据。

    第一次 for 循环在写头时程序没有进入 while 循环(端口 6 上的 LED 不会亮)。

非常感谢您提供任何帮助,代码如下。

#define DDR_SPI DDRB
#define DD_MISO DDB4
#define DD_MOSI DDB3
#define DD_SCK DDB5
#define DD_SS DDB6

void SPI_MasterInit(void)

    /* Set MOSI, SCK and CS output, all others input */
    DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
    /* Enable SPI, Master, set clock rate = System clock / 16 */
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);


int readfromspilow(uint16 headerLength, const uint8 *headerBuffer, uint32 readLength, uint8 *readBuffer)



    PORTB &= ~(1 << PORTB6);                // Set CS low

    for(int i=0; i<headerLength; i++)
    
        SPDR = headerBuffer[i];             //Send entry i of the header to the spi register
        sleep_us(5);                        // Give the flag time to reset, might be unnecessary
        PORTD |= (1 << PORTD5);             //LED for diagnostics
        while(!(SPSR & (1<<SPIF)))         //Wait for SPIFinished flag
            PORTD |= (1 << PORTD6);         //LED for diagnostics
        

        PORTD &= ~(1 << PORTD3);            //LED for diagnostics



        //readBuffer[0] = SPDR; // Dummy read as we write the header
    

    for(int i=0; i<readLength; i++)
    
        SPDR = 0xFF;                        // Dummy write as we read the message body
        sleep_us(5);                        // Give the flag time to reset, might be unnecessary
        while(!(SPSR & (1<<SPIF)));         //Wait for SPIFinished flag
        readBuffer[i] = SPDR ;              //Store the value in the buffer
    

    PORTB |= (1 << PORTB6);                 // Set CS high


    return;

编辑:为初始化添加了定义

【问题讨论】:

你以后如何在代码中使用它?什么是 DDR_SPI 和其他?位匹配?等待 SPI 完成的 while 循环会停止吗? 我们只需调用 readfromspilow ,将所需命令作为标头(地址等我们希望读取的寄存器)和预期答案的长度作为读取长度,并带有一个缓冲区指向的点存储阅读的答案。我通过编辑添加了 SPI 的定义,不确定“位匹配”是什么意思。 while 在第一次迭代中被跳过(不知道为什么),但在下一次迭代中被卡住了(大概是因为 SPI 从未发出完成信号) 【参考方案1】:

根据手册 19.5.2

或者,SPIF 位通过首先读取 SPI 状态来清除 注册并设置 SPIF,然后访问 SPI 数据寄存器 (SPDR)。

您的代码没有这样做,所以可能有一个标志集永远不会被清除。尝试将代码更改为:

volatile uint8_t spsr = SPSR;       //Dummy-read the flag register to clear flags
SPDR = headerBuffer[i];             //Send entry i of the header to the spi register
PORTD |= (1 << PORTD5);             //LED for diagnostics
while(!(SPSR & (1<<SPIF)))         //Wait for SPIFinished flag
  PORTD |= (1 << PORTD6);           //LED for diagnostics

【讨论】:

【参考方案2】:

您的代码似乎是正确的,但由于您没有使用 SPI 中断处理程序,因此您需要在通过 SPDR 发送数据之前清除 SPSR 中的 SPIF 位。 SPDR 不接受任何设置了 SPIF 标志的数据。

【讨论】:

【参考方案3】:

我们解决了,问题是在初始化阶段需要将端口B2上的默认SS定义为输出,否则如果引脚悬空,MCU可以切换到Slave操作模式。这会在每次发生时将 SPIF 标志设置为 1,这会干扰我们的检查。

【讨论】:

以上是关于atmega328 上的 SPI 不工作的主要内容,如果未能解决你的问题,请参考以下文章

Atmega328 模数转换器

ATMEGA328P(Arduino Pro Mini)超低运行功耗探索

AVR - 高速中断驱动的 UART 代码不工作

Atmel Studio 烧录 Atmega328P(Arduiono)

ATMEGA2561 WINC1500驱动实现SPI问题

使用 UDRE 和 ATmega328P 的中断驱动 USART