4

ATtiny88初体验(六):SPI - chinjinyu

 1 year ago
source link: https://www.cnblogs.com/chinjinyu/p/17677594.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

ATtiny88初体验(六):SPI

SPI介绍#

ATtiny88自带SPI模块,可以实现数据的全双工三线同步传输。它支持主从两种模式,可以配置为LSB或者MSB优先传输,有7种可编程速率,支持从空闲模式唤醒。

image.png

注意:为了使用SPI模块,必须将 PRR 寄存器中的 PRSPI 位设置为0。

ATtiny88的SPI时钟频率不能超过 fOSC/4 ,双倍速率模式下不能超过 fOSC/2 。

当SPI使能时,MOSI、MISO、SCK、SS引脚的方向会被覆盖,具体见下表:

image.png

根据SCK的极性和相位不同,SPI分为四种模式:

image.png

image.png

image.png

寄存器#

image.png
  • SPIE :写入1使能SPI中断。
  • SPE :写入1使能SPI。
  • DORD :数据方向,写入1为LSB优先,写入0为MSB优先。
  • MSTR :主机/从机模式选择,写入1为主机模式,写入0为从机模式。
  • CPOL :时钟极性。
  • CPHA :时钟相位。
  • SPR[1:0] :SPI时钟速率选择。
    image.png

image.png
  • SPIF :SPI中断标志,执行完中断后自动清除,或者通过先读 SPSR 寄存器,再访问 SPDR 寄存器清除。
  • WCOL :写冲突标志,在数据传输期间对 SPDR 寄存器进行写操作时置位,通过先读 SPSR 寄存器,再访问 SPDR 寄存器清除。
  • SPI2X :SPI速率加倍。在主机模式下,向此位写入1使SPI时钟速率加倍,最大速率为 fOSC/2 。在从机模式下,最大速率还是只有 fOSC/4 。

image.png

代码#

下面的代码演示了使用ATtiny88的SPI模块与W25Q32 Flash模块进行通信,读取Flash的ID信息。源文件的组织结构如下:

.
├── Makefile
├── inc
│   ├── serial.h
│   └── serial_stdio.h
└── src
    ├── main.c
    ├── serial.c
    └── serial_stdio.c

src/main.c 源文件的代码如下:

#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <serial_stdio.h>

static void spi_setup(void);
static uint8_t spi_read_and_write(uint8_t data);
static void w25qxx_read_device_id(void *id, uint8_t n);
static void w25qxx_read_manufacturer_device_id(void *id, uint8_t n);
static void w25qxx_read_unique_id(void *id, uint8_t n);
static void w25qxx_read_jedec_id(void *id, uint8_t n);

int main(void)
{
    cli();
    stdio_setup();              // initialize stdio and redirect it to serial
    spi_setup();                // initialize spi module
    sei();

    printf("=================================\r\n");

    // read device id of spi flash
    uint8_t buf[8];
    w25qxx_read_device_id(buf, 1);
    printf("device id: 0x%02X.\r\n", buf[0]);

    // read manufacturer and device id of spi flash
    w25qxx_read_manufacturer_device_id(buf, 2);
    printf("manufacturer & device id: 0x%02X%02X.\r\n", buf[0], buf[1]);

    // read unique id of spi flash
    w25qxx_read_unique_id(buf, 8);
    printf("unique id: 0x");
    for (uint8_t i = 0; i < 8; i++) {
        printf("%02X", buf[i]);
    }
    printf(".\r\n");

    // read jedec id of spi flash
    w25qxx_read_jedec_id(buf, 3);
    printf("jedec id: 0x%02X%02X%02X.\r\n", buf[0], buf[1], buf[2]);

    for (;;);
}

static void spi_setup(void)
{
    // initialize gpios
    // PB2 -> SS
    // PB3 -> MOSI
    // PB4 -> MISO
    // PB5 -> SCK
    DDRB |= _BV(DDB2) | _BV(DDB3) | _BV(DDB5);
    PORTB |= _BV(PORTB2) | _BV(PORTB3) | _BV(PORTB5);

    // enable spi, msb first, master mode, mode 3, prescaler = 64
    SPCR = _BV(SPE) | _BV(MSTR) | _BV(CPOL) | _BV(CPHA) | _BV(SPR1) | _BV(SPR0);
    SPSR = _BV(SPI2X);
}

static uint8_t spi_read_and_write(uint8_t data)
{
    SPDR = data;
    while (!(SPSR & _BV(SPIF)));
    return SPDR;
}

static void w25qxx_read_device_id(void *id, uint8_t n)
{
    if (n > 1) {
        n = 1;
    }

    PORTB &= ~_BV(PORTB2);
    spi_read_and_write(0xAB);
    spi_read_and_write(0xFF);
    spi_read_and_write(0xFF);
    spi_read_and_write(0xFF);
    while (n--) {
        *(uint8_t *)id++ = spi_read_and_write(0xFF);
    }
    PORTB |= _BV(PORTB2);
}

static void w25qxx_read_manufacturer_device_id(void *id, uint8_t n)
{
    if (n > 2) {
        n = 2;
    }

    PORTB &= ~_BV(PORTB2);
    spi_read_and_write(0x90);
    spi_read_and_write(0xFF);
    spi_read_and_write(0xFF);
    spi_read_and_write(0x00);
    while (n--) {
        *(uint8_t *)id++ = spi_read_and_write(0xFF);
    }
    PORTB |= _BV(PORTB2);
}

static void w25qxx_read_unique_id(void *id, uint8_t n)
{
    if (n > 8) {
        n = 8;
    }

    PORTB &= ~_BV(PORTB2);
    spi_read_and_write(0x4B);
    spi_read_and_write(0xFF);
    spi_read_and_write(0xFF);
    spi_read_and_write(0xFF);
    spi_read_and_write(0xFF);
    while (n--) {
        *(uint8_t *)id++ = spi_read_and_write(0xFF);
    }
    PORTB |= _BV(PORTB2);
}

static void w25qxx_read_jedec_id(void *id, uint8_t n)
{
    if (n > 3) {
        n = 3;
    }

    PORTB &= ~_BV(PORTB2);
    spi_read_and_write(0x9F);
    while (n--) {
        *(uint8_t *)id++ = spi_read_and_write(0xFF);
    }
    PORTB |= _BV(PORTB2);
}

编译并下载程序到ATtiny88,连接好串口,可以观察串口的输出如下:

image.png

参考资料#


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK