B001-Atmega16-SPI

0
0



0
主要内容:
第一步:SPI的结构
第二步:SPI的初始化
第三步:Atmega16的SPI自发自收
第四步:与SPI Flash连接
第五步:读取SPI Flash的RDID
第六步:读写SPI Flash的接口


-------------------------------------------------------------------------------------------------------------------------------------
开发环境:AVR Studio 4.19 + avr-toolchain-installer-3.4.1.1195-win32.win32.x86
芯片型号:ATmega16
芯片主频:8MHz


-------------------------------------------------------------------------------------------------------------------------------------

第一步: SPI的结构

1、下面是一个主机 MASTER和一个从机 SLAVE连接时的情况:
B001-Atmega16-SPI_第1张图片
主机和从机中都各自有一个 8bit的移位寄存器,在时钟的驱动下进行移位操作,这有点像汇编中循环移位指令的执行。
输入输出引脚:
SS:主机通过拉低从机的 SS引脚,来使能从机的 SPI接口; SS引脚平时为高电平。
SCLK:主机发出时钟信号 SCLK,来驱动移位寄存器的数据移位,通常是一个时钟的上升沿锁存数据,下降沿移出数据。
MISO:主机接收 来自 从机的数据
MOSI:主机发送 数据 到从机,实质就是用 MISOMOSI两条线、将两个移位寄存器连接成一个 16bit循环移位寄存器

工作过程:
1、主机拉低 SS#引脚电平,使能从机。
2、首先、在主机和从机的移位寄存器里面,分别放入各自的数据(不放入的话、里面当然也会有一个初始的随机数)。
3、 SCLK开始发送时钟信号
4、在每个时钟脉冲内、主机和从机都完成 1次数据移位,互相交换了 1bit(详细地说就是:上升沿锁存数据、下降沿移出数据)。
5、连续8个时钟脉冲后、主机和从机通过移位互换了各自的 8bit数据,通过读取各自的移位寄存器、可以得到它们交换到的数据。
6、主机拉高 CS#引脚电平,结束本次数据交换,本次共完成了 8bit数据交换,如果需要交换更多数据,连续发送 SCLK时钟即可。

● 所以说, SPI通信的本质就是主机和从机的数据互换、通过移位的方式。

-------------------------------------------------------------------------------------------------------------------------------------

第二步: SPI的初始化

1、 SPI初始化过程主要是做如下几件事:
● 选择哪个SPI接口作为主机,哪个作为从机
● 设置SPI的 SCLK时钟的频率
● 选择是左移(先发送最高位MSB)还是右移(先发送最高位LSB)
● 设置是上升沿移位还是下降沿移位
● 选择是上升沿锁存还是下降沿锁存(数据一般是先锁存再移位,这就涉及到移位寄存器的硬件操作)
● 设置SPI引脚(MOSI和MISO引脚的方向要看数据移位的方向来定:移出就是输出引脚、移入就是输入引脚)
● 选择是否开启传输完成中断(每次完成了 8bit的数据移位锁存后、就会产生1次中断)

这里使用下面定义的初始化函数来进行初始化:
void Drv_SPI_init(const uint8_t device_mode, const uint8_t shift_mode, const uint8_t shift_first, const uint8_t clk_source);
设置参数如下:
● device_mode    主从模式选择(SPI_MODE_MSTR, SPI_MODE_SLAVE)  
● shift_mode       锁存/移位模式  
● shift_first          首先移出MSB(左移)还是LSB(右移)
● clk_source        SCLK频率 

2、根据 Atmega16的Datasheet、 SPI的初始化接口如下:
Drv_SPI.h
// ==========================================================================================================
// Copyright (c) 2016 Chen.Chen <[email protected]>
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
// associated documentation files (the "Software"), to deal in the Software without restriction, including 
// without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 
// sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject 
// to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// ---------------------------------
// 本文定义了Atmega16下的SPI初始化
// 
// ==========================================================================================================
#ifndef __DRV_SPI_H__
#define __DRV_SPI_H__


#include <avr/io.h>
#include <avr/interrupt.h>

#include "Drv_IO_Port.h"
#include "config.h"


// SPI的传输模式(CPOL:时钟极性)(CPHA:采样边沿)
// ---------------------------------------------------
// mode | CPOL | CPHA |      前沿      |      后沿
// ---------------------------------------------------
//  0   |  0   |  0   |  采样锁存(↑)  |  移出    (↓)
// ---------------------------------------------------
//  1   |  0   |  1   |  移出    (↑)  |  采样锁存(↓)
// ---------------------------------------------------
//  2   |  1   |  0   |  采样锁存(↓)  |  移出    (↑)
// ---------------------------------------------------
//  3   |  1   |  1   |  移出    (↓)  |  采样锁存(↑)
// ---------------------------------------------------
typedef enum 
{
    SPI_SHIFT_FIRST_MSB = 0,
    SPI_SHIFT_FIRST_LSB = 1,

    SPI_MODE_SLAVE = 0,
    SPI_MODE_MSTR  = 1,

    SPI_SHIFT_MODE_00 = 0,  // 传输模式
    SPI_SHIFT_MODE_01 = 1,
    SPI_SHIFT_MODE_02 = 2,
    SPI_SHIFT_MODE_03 = 3,

    SPI_CLK_SOURCE_DIV_2   = 0,  // SCLK频率的分频系数
    SPI_CLK_SOURCE_DIV_4   = 1,
    SPI_CLK_SOURCE_DIV_8   = 2,
    SPI_CLK_SOURCE_DIV_16  = 3,
    SPI_CLK_SOURCE_DIV_32  = 4,
    SPI_CLK_SOURCE_DIV_64  = 5,
    SPI_CLK_SOURCE_DIV_128 = 6
} DRV_SPI_SETTINHG;


extern volatile uint8_t SPI_shift_end;
#define SPI_SHIFT_END   SPI_shift_end

void Drv_SPI_init(const uint8_t device_mode, const uint8_t shift_mode, const uint8_t shift_first, const uint8_t clk_source);
void Drv_SPI_INT_Enable(const uint8_t enable);


#endif  // #ifndef __DRV_SPI_H__
Drv_SPI.c
// ==========================================================================================================
// Copyright (c) 2016 Chen.Chen <[email protected]>
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
// associated documentation files (the "Software"), to deal in the Software without restriction, including 
// without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 
// sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject 
// to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// ---------------------------------
// 本文定义了Atmega16下的SPI初始化
// 
// ==========================================================================================================
#include "Drv_SPI.h"


volatile uint8_t temp2017 = 0;  // 调试用
volatile uint8_t temp2018 = 0;

volatile uint8_t SPI_shift_end = 1; // 0=SPI传输未完成,1=SPI传输完成

// ==========================================================================================================
// SPI初始化
// 
// 参数:device_mode    主从模式(SPI_MODE_MSTR, SPI_MODE_SLAVE)
//       latch_mode     锁存/移位模式
//       shift_first    首先移出MSB还是LSB
//       clk_source     SCLK频率
// 
// 说明:
// (1). 当MSTR=1 && SS引脚为输入IO时,如果SS引脚被拉低,将导致MSTR=0 && SPIF=1,使得机器变成从机模式
// (2). SPI Flash.GD25Q32支持模式0和模式3
// 
// ==========================================================================================================
void Drv_SPI_init(const uint8_t device_mode, const uint8_t shift_mode, const uint8_t shift_first, const uint8_t clk_source)
{
    uint8_t spr10;
    uint8_t spi2x;
    uint8_t cpol;
    uint8_t cpha;

    switch(shift_mode)
    {
        case SPI_SHIFT_MODE_00: cpol = 0; cpha = 0;
                                break;
        case SPI_SHIFT_MODE_01: cpol = 0; cpha = 1;
                                break;
        case SPI_SHIFT_MODE_02: cpol = 1; cpha = 0;
                                break;
        case SPI_SHIFT_MODE_03: cpol = 1; cpha = 1;
                                break;
        default : return;
    }
    switch(clk_source)
    {
        case SPI_CLK_SOURCE_DIV_2:   spr10 = 0; spi2x = 1;
                                     break;
        case SPI_CLK_SOURCE_DIV_4:   spr10 = 0; spi2x = 0;
                                     break;
        case SPI_CLK_SOURCE_DIV_8:   spr10 = 1; spi2x = 1;
                                     break;
        case SPI_CLK_SOURCE_DIV_16:  spr10 = 1; spi2x = 0;
                                     break;
        case SPI_CLK_SOURCE_DIV_32:  spr10 = 2; spi2x = 1;
                                     break;
        case SPI_CLK_SOURCE_DIV_64:  spr10 = 2; spi2x = 0;
                                     break;
        case SPI_CLK_SOURCE_DIV_128: spr10 = 3; spi2x = 0;
                                     break;
        default : return;
    }

    // IO设置
    if(SPI_MODE_MSTR == device_mode)
    {
        Drv_IO_set_bit(PORTB, PB4);
        Drv_IO_mode_bit(DDRB, PB4, IO_OUTPUT); // SS输出(拉高)、以禁止SPI从机
        Drv_IO_clr_bit(PORTB, PB5);
        Drv_IO_mode_bit(DDRB, PB5, IO_OUTPUT); // MOSI输出(拉低)
        Drv_IO_clr_bit(PORTB, PB6);
        Drv_IO_mode_bit(DDRB, PB6, IO_INPUT);  // MISO输入(拉低)
        Drv_IO_clr_bit(PORTB, PB7);
        Drv_IO_mode_bit(DDRB, PB7, IO_OUTPUT); // SCLK输出(拉低)
    }
    else
    {
        Drv_IO_set_bit(PORTB, PB4);
        Drv_IO_mode_bit(DDRB, PB4, IO_INPUT);  // SS输入(拉高|如果PUD使能、将打开上拉)
        Drv_IO_clr_bit(PORTB, PB5);
        Drv_IO_mode_bit(DDRB, PB5, IO_INPUT);  // MOSI输入(拉低)
        Drv_IO_clr_bit(PORTB, PB6);
        Drv_IO_mode_bit(DDRB, PB6, IO_OUTPUT); // MISO输出(拉低)
        Drv_IO_clr_bit(PORTB, PB7);
        Drv_IO_mode_bit(DDRB, PB7, IO_INPUT);  // SCLK输入(拉低)
    }

    // 初始化
    SPCR = ( 1 << SPE ) |
           ((shift_first & 0x01) << DORD) | // 先发送MSB或LSB
           ((device_mode & 0x01) << MSTR) | // 主从模式
           ((cpol        & 0x01) << CPOL) | // 时钟极性
           ((cpha        & 0x01) << CPHA) | // 前后沿
           ((spr10       & 0x03) << SPR0) ; // 时钟频率

    SPSR &= ~(1     << SPI2X);
    SPSR |=  (spi2x << SPI2X);               // 时钟倍频

    SPI_shift_end = 1;
}

// ==========================================================================================================
// SPI传输完成中断使能
// 
// 说明:
// (1). 清0 SPIF的方法:先读SPSR、在访问SPDR,可清0
// (2). 为了避免temp被优化掉,增加了volatile修饰、同时将temp的值返回
// 
// ==========================================================================================================
void Drv_SPI_INT_Enable(const uint8_t enable)
{
    volatile uint8_t temp;

    if(DISABLE == enable)
    {
        SPCR &= ~(1 << SPIE);
    }
    else
    {
        SPCR |=  (1 << SPIE);
    }

    temp  = SPSR;
    temp += SPDR;
}

// ==========================================================================================================
// SPI传输完成中断
// 
// 说明:
// (1). 使用主机模式
// 
// ==========================================================================================================
ISR(SPI_STC_vect)
{
    SPI_shift_end = 1;
    temp2017++;
    temp2018 = SPDR;

    // 主机模式下,如果SS引脚配置为输入、SS引脚收到低电平、就会导致SPI接口自动变为从机模式
    // 所以这里需要检查主机模式
    if(SPI_MODE_SLAVE == (SPCR & (1 << MSTR)))
    {
        SPCR |= (SPI_MODE_MSTR << MSTR);
        return;
    }
}


-------------------------------------------------------------------------------------------------------------------------------------

第三步: Atmega16的SPI自发自收

1、既然 SPI结构核心就是个移位寄存器,那么主机不必与从机连接成一个 循环移位寄存器,主机自己首尾相连、也是可以进行循环移位操作的。
    也就是说、主机自己首尾相连、成为一个 8bt循环移位寄存器,当然也可以自发自收。
主机如下连接:
B001-Atmega16-SPI_第2张图片

2、我们将SPI初始化如下(设置为主机模式、先发送最高位MSB、SCLK时钟的频率为主时钟的128分频=62.5KHz):
#define Drv_SPI_set_SS()            Drv_IO_set_bit(PORTB, PB4)
#define Drv_SPI_clr_SS()            Drv_IO_clr_bit(PORTB, PB4)

Drv_SPI_init(SPI_MODE_MSTR, SPI_SHIFT_MODE_00, SPI_SHIFT_FIRST_MSB, SPI_CLK_SOURCE_DIV_128);
Drv_SPI_INT_Enable(ENABLE);
然后、让SPI发送一个8bit数据:
    Drv_SPI_clr_SS();

    SPI_SHIFT_END = 0;
    SPDR = 0x9F;
    while(0 == SPI_SHIFT_END) {}
    temp2016 = SPDR;

    Drv_SPI_set_SS();
测试如下:


(1)、 CH2是SCLK信号,图中两个鼠标竖线之间的5个脉冲总共是12.38KHz,那么每个脉冲大概就是 62KHz,和设置的基本一样。
(2)、 CH1是MOSI信号,我们发送的是 0x9F=0b10011111
       在上图中、8个脉冲对应的数值正好就是 0b10011111= 0x9F
(3)、在发送数据时、我们是需要在程序中将数据写入寄存器SPDR、SPI接口的SCLK信号就会 自动产生
       并在8bit数据发送结束后 自动停止SCLK信号。
(4)、MOSI引脚在空闲时就是高电平、也就是在发送前和发送后都是保持高电平。
       在Datasheet里面虽然说MOSI的引脚方向由用户程序定义,但MOSI的电平却是由SPI接口自己决定。
       在时序图中、MOSI在空闲时是高电平,而MISO是输入、电平与从机输出的电平保持一致:

(5)、在Debug下、SPDR接收到我们发送的数据0x9F:


3、我们也可以将从MOSI输出的数据 0x9F反向后、再输入MISO引脚:

确实可以得到反向后的数据(0x9F反向后、得到0x60):
(1)、反向后的波形:

(2)、Debug下可看到SPDR中接收到反向后的数值0x60:



-------------------------------------------------------------------------------------------------------------------------------------

第四步: 与SPI Flash连接

1、下面我们把SPI Flash接到Atmega16的SPI接口,让Atmega16读写SPI Flash。
     电路如下:

因为我们使用的GD25Q32是一个3V驱动的SPI Flash芯片,而Atmega16是5V芯片。所以这里给出了3.3V电源,同时简单的做了一个3V-5V的转换电路。
从Atmega16输出的信号CS、MOSI、SCLK都简单的用电阻和GD25Q32连接,而从GD25Q32输出到Atmega16的MISO、使用了一个3V-5V的转换电路。
原因如下表:
B001-Atmega16-SPI_第3张图片

(1)、GD25Q32的输入引脚接Atmega16的输出引脚的情况:
 ●    GD25Q32的输入低电平VIL要求在0.66V以下,而Atmega16输出的低电平VOL是在0.7V以下。
                     然而低电平一般都可以保证接近0V,因此可以认为Atmega16输出的低电平VOL接近0V、能够满足GD25Q32的低电平VIL需要在0.66V以下的要求。
 ●    GD25Q32的输入高电平VIH要求在2.31V以上,而Atmega16 输出的高电平VOH是在4.2V以上。
                     Atmega16输出的高电平、可以满足GD25Q32的高电平需要在2.31V以上的要求,只是电压过高会有损使用寿命。

(2)、GD25Q32的输出引脚接Atmega16的输入引脚的情况:
 ●    GD25Q32的输出低电平VOL是在0.4V以下,而Atmega16的输入低电平VIL要求在1.0V以下。
                     GD25Q32的输出低电平、可以满足Atmega16的低电平VIL需要在1.0V以下的要求。
 ●    GD25Q32的输出高电平是在3.1V以上,而Atmega16要求在3.0V以上。
                     GD25Q32的输出高电平、勉强可以满足Atmega16的高电平VIH在3.0V以上的要求。
                     只不过在电源波动情况下、GD25Q32的输出高电平可能会低于3.1V,所以最好加一个3V-5V的转换电路。

当然、为了保证可靠、避免因电源、器件生成工艺带来的误差,最好在每个引脚上都加上电平转换电路或转换芯片。
直接将3V-5V器件连接、波形传输过程中得不到正确的波形。


另外、Atmega16使用8MHz的主时钟、而SPI接口的SCLK最大可以到主时钟的一半、也就是4MHz。
这个频率下、S8050和S8550是否能响应,还需测试,所以一开始时使用最低频率:SCLK=主时钟的128分频=62.5KHz。
这里三极管都没有加上下拉,测试结果还可以。


-------------------------------------------------------------------------------------------------------------------------------------

第五步:读取SPI Flash的RDID

1、Atmega16连接好SPI Flash之后,我们先读取SPI Flash,看看工作情况如何。
     首先选择读取SPI Flash的RDID,GD25Q32的Datasheet中描述了读取RDID的过程:
B001-Atmega16-SPI_第4张图片
步骤如下:
(1)、主机将CS#拉低、以使能从机。
(2)、写入读RDID指令0x9F到SPDR。
      (此时会自动启动SCLK时钟,开始主机和从机之间的数据移位交换)
(3)、等待SPDR中的8bit指令数据发送完毕(此时会自动停止SCLK时钟) 
      (指令发送完毕后、GD25Q32内部会对指令0x9F进行指令译码,并将24bit的RDID寻址到缓存里面以供读取)
(4)、向SPDR里面写入一个无意义的数值(0x00)、以启动SCLK时钟、来开始主机和从机之间的数据移位交换。
      (只有向SPDR写入数据,才能启动SCLK时钟,所以需要写入并发送0x00、来和GD25Q32交换数据,而GD25Q32不会响应0x00这个数据)
(5)、等待SPDR发送完毕,此时我们完成了第一个8bit数据的交换,需要将其保存。
(6)、再联系重复第(4)步两次,交换完剩下的两个8bit数据,并保存他们。
(7)、将得到的三个8bit数据组合成最终的24bit数据。

代码如下:
指令数据(Mod_SPI_Flash.h):
// 以GD25Q32B指令集为基准建立
typedef enum 
{
    SPI_COMMAND_DUMMY         = 0x00,   // 无效数据,用于启动SCLK发送

    SPI_COMMAND_WRITE_ENABLE  = 0x06,
    SPI_COMMAND_WRITE_DISABLE = 0x04,
    SPI_COMMAND_READ_STATUS   = 0x05,   // 状态寄存器的低8位
    SPI_COMMAND_READ_STATUS_H = 0x35,   // 状态寄存器的高8位
    SPI_COMMAND_WRITE_STATUS  = 0x01,

    SPI_COMMAND_READ          = 0x03,
    SPI_COMMAND_PAGE_PROGRAM  = 0x02,
    SPI_COMMAND_SECTOR_ERASE  = 0x20,
    SPI_COMMAND_BLOCK_ERASE32 = 0x52,   // 块擦除(32K)
    SPI_COMMAND_BLOCK_ERASE64 = 0xD8,   // 块擦除(64K)
    SPI_COMMAND_CHIP_ERASE    = 0xC7,   // 或0x60

    SPI_COMMAND_READ_RDID      = 0x9F,  // 生产商标识(GD25Q32 = 0xC84016)
//  SPI_COMMAND_READ_ID_MANU   = 0x90,  // 生产商ID(GD25Q32 = 0xC815)
//  SPI_COMMAND_READ_ID        = 0xAB,  // 器件ID(GD25Q32 = 15)(同时也是唤醒指令)
//  SPI_COMMAND_SLEEP          = 0XB9,  // 休眠

//  SPI_COMMAND_CON_READ_RESET = 0xFF,  // 连续读操作复位
//  SPI_COMMAND_ERASE_SECURITY = 0x44,  // 擦除security寄存器
//  SPI_COMMAND_WRITE_SECURITY = 0x42,  // 写入security寄存器
//  SPI_COMMAND_READ_SECURITY  = 0x48,  // 读取security寄存器
//  SPI_COMMAND_SUSPEND        = 0x75,  // 编程/擦除延迟
//  SPI_COMMAND_RESUME         = 0X7A,  // 编程/擦除恢复
//  SPI_COMMAND_HP_MODE        = 0xA3,  // 高性能模式

//  SPI_COMMAND_READ_FAST     = 0x0B,
//  SPI_COMMAND_DUAL_READ_OUT = 0x3B,
//  SPI_COMMAND_DUAL_READ_IO  = 0xBB,

//  SPI_COMMAND_QUAD_READ_OUT  = 0x6B,
//  SPI_COMMAND_QUAD_READ_IO   = 0xEB,
//  SPI_COMMAND_QUAD_READ_WORD = 0xE7,
//  SPI_COMMAND_QUAD_PAGE_PROGRAM = 0x32,
}FLASH_COMMAND_SET;
读取函数( Mod_SPI_Flash.c):
// ==========================================================================================================
// 读器件ID(GD25Q32的RDID是一个24bit数值)
// 
// ==========================================================================================================
uint32_t SPI_Flash_read_RDID(void)
{
    uint32_t data = 0;

    Drv_SPI_clr_SS();

    SPI_SHIFT_END = 0;
    SPDR = SPI_COMMAND_READ_RDID;   // 读取RDID的指令
    while(0 == SPI_SHIFT_END) {}
    temp2016 = SPDR;

    SPI_SHIFT_END = 0;
    SPDR = SPI_COMMAND_DUMMY;       // 重新发出SCLK(发送无意义数据0x00)、以读取第一个8bit数据
    while(0 == SPI_SHIFT_END) {}
    data = SPDR;
    temp2016 = data;

    SPI_SHIFT_END = 0;
    SPDR = SPI_COMMAND_DUMMY;
    while(0 == SPI_SHIFT_END) {}
    data = (data << 8) | SPDR;
    temp2016 = data;

    SPI_SHIFT_END = 0;
    SPDR = SPI_COMMAND_DUMMY;
    while(0 == SPI_SHIFT_END) {}
    data = (data << 8) | SPDR;
    temp2016 = data;

    Drv_SPI_set_SS();

    temp2016 = (~temp2016)&0x00FFFFFF;

    return (~data)&0x00FFFFFF;
}
其中、变量SPI_SHIFT_END会在中断中设置(当然也可以直接检测SPIF位、然后在发送前清除SPIF位):
// ==========================================================================================================
// SPI传输完成中断
// 
// 说明:
// (1). 使用主机模式
// 
// ==========================================================================================================
ISR(SPI_STC_vect)
{
    SPI_SHIFT_END = 1;
    temp2017++;
    temp2018 = SPDR;

    // 主机模式下,如果SS引脚配置为输入、SS引脚收到低电平、就会导致SPI接口自动变为从机模式
    // 所以这里需要检查主机模式
    if(SPI_MODE_SLAVE == (SPCR & (1 << MSTR)))
    {
        SPCR |= (SPI_MODE_MSTR << MSTR);
        return;
    }
}
在Debug下可以得到这个24bit的RDID=0x00C84016:

这和GD25Q32的Datasheet里面给出的数值一致:
B001-Atmega16-SPI_第5张图片
这表明我们读取SPI Flash的步骤是对的。

而测试到的波形也印证了这一点:
B001-Atmega16-SPI_第6张图片
在图中、在发送完0x9F的指令后,连续对GD25Q32进行了7次读取,读到的都是0xC84016这个数值:
B001-Atmega16-SPI_第7张图片
这说明、在GD25Q32对指令数据0x9F译码后,RDID的数值(0xC84016)被寻址到缓存。
我们每次都去、都会在缓存里面得到相同的一个数值。


-------------------------------------------------------------------------------------------------------------------------------------

第六步:读写SPI Flash的接口

1、最后给出读写SPI Flash的接口
0

0

你可能感兴趣的:(SPI接口读写,GD25Q32的读写,Atmega16的SPI接口,3V-5V电平转换)