Microchip SPI SRAM 23LC1024 demo

Dependencies:   mbed

main.cpp

Committer:
discypus
Date:
2015-11-09
Revision:
3:b585283f5932
Parent:
2:e0d7ea47d2a5

File content as of revision 3:b585283f5932:

/*
 * sample for Microchip 23LC1024 (SRAM, SPI 20MHz)
 */

#include "mbed.h"

#define DWT_CONTROL ((volatile uint32_t *)0xE0001000)
#define DWT_CYCCNT  ((volatile uint32_t *)0xE0001004)
#define SCB_DEMCR   ((volatile uint32_t *)0xE000EDFC)

static inline void enableDwtCyccnt()
{
//    const uint32_t NOCYCCNT = 1u << 25; // 1:CYCCNT is NOT support
    const uint32_t TRCENA = 1u << 24;   // enable DWT
    const uint32_t CYCCNTENA = 1u << 0;    // enable counter
    
//    if (*DWT_CONTROL & NOCYCCNT) { // F401REではsetされていたが、動作する。
        *SCB_DEMCR |= TRCENA;
        *DWT_CONTROL |= CYCCNTENA;
        *DWT_CYCCNT = 0; // reset the counter
//    } else {
        // NOT supported DWT_CYCCNT
//        pc.printf("NOT supported DWT_CYCCNT\r\n");
//    }
}
static inline uint32_t getDwtCyccnt(void)
{
    return *DWT_CYCCNT;
}

/** Serial port for DEBUG */
Serial pc(USBTX, USBRX);

DigitalIn user_button(USER_BUTTON);

/** */
SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK);

/** */
DigitalOut cs(SPI_CS);

/**
 * USERボタンが押されるまで待つ。
 */
void
wait_until_user_button_pressed() {
    while (user_button) {
        __nop();
    }
    pc.printf("USER button is pressed\r\n");
    wait(0.2);
}

/**
 * 動作確認のために、spiオブジェクトを直接使って読み書きする。
 * @pre SPI初期化ずみであること。
 */
void
try_single_byte_access() {
    const uint32_t address = 0u;
    const uint8_t address_high = (address >> (8 * 2)) & 0xFFu;
    const uint8_t address_mid  = (address >> (8 * 1)) & 0xFFu;
    const uint8_t address_low  = (address >> (8 * 0)) & 0xFFu;

    const uint8_t command_write = 0x02u;
    const uint8_t command_read = 0x03u;
    
    const uint8_t data = 0x55;

    static uint32_t cpuCounter[100] = {};
    size_t counterIndex = 0;
    enableDwtCyccnt();
    
    // 1byte write
    cpuCounter[counterIndex++] = getDwtCyccnt();
    cs = 0;                             cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(command_write);           cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(address_high);            cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(address_mid);             cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(address_low);             cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(data);                    cpuCounter[counterIndex++] = getDwtCyccnt();  // 3:CS Hold Time 50ns(min)=1cycle(20MHz)
    cs = 1;                             cpuCounter[counterIndex++] = getDwtCyccnt();
    
    // 1byte read
    cs = 0;                             cpuCounter[counterIndex++] = getDwtCyccnt();  // 2:CS setup time 25ns(min)
    spi.write(command_read);            cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(address_high);            cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(address_mid);             cpuCounter[counterIndex++] = getDwtCyccnt();
    spi.write(address_low);             cpuCounter[counterIndex++] = getDwtCyccnt();
    const uint8_t value = spi.write(0); cpuCounter[counterIndex++] = getDwtCyccnt();  // 3:CS Hold Time 50ns(min)
    cs = 1;                             cpuCounter[counterIndex++] = getDwtCyccnt();

    pc.printf("single byte access test: result\r\n");
    pc.printf("  write: %02x -> read: %02x\r\n", data, value);

    pc.printf("CPU Cycle counter\r\n");
    for (int i = 1; i < counterIndex; i++) {
        const float ns = (cpuCounter[i] - cpuCounter[i - 1]) / (float)SystemCoreClock * 1.0e9;
        pc.printf("  %02d = %.3f\r\n", i, ns);
    }
}

/*
 * SPI COMMAND (Microchip 23LC1024, 23K256 SPI SRAM)
 */
enum Microchip23LC1024Commnd {
    READ  = 0x03u,
    WRITE = 0x02u,
    EDIO  = 0x3bu,
    EQIO  = 0x38u,
    RSTIO = 0xffu,
    RDMR  = 0x05u,
    WRMR  = 0x01u,
};

/** SRAM Mode (Microchip 23LC1024, 23K256 SPI SRAM) */
enum Microchip23LC1024Mode {
    MODE_MASK  = 0xc0u,
    BYTE       = 0x00u,
    SEQUENTIAL = 0x40u, // default operation
    PAGE       = 0x80u,
    RESERVED   = 0xC0u,
};

/**
 * assert CS
 */
inline void
assert_CS() {
    cs = 0;
}

/**
 * negate CS
 */
inline void
negate_CS() {
    cs = 1;
}

/**
 * モードレジスタ読みだし
 * @return レジスタ値
 */
inline uint8_t
read_mode_register() {
    assert_CS();
    spi.write(RDMR);
    const uint8_t mode = spi.write(0x00);
    negate_CS();
    
    return mode;
}

/**
 * モードレジスタ書き込み
 * @param[in] mode
 */
inline void
write_mode_register(const uint8_t mode) {
    assert_CS();
    spi.write(WRMR);
    spi.write(mode);
    negate_CS();
}

/**
 * モードを切り替える。
 * @param[in] mode モード
 * @return 切替前のモード
 * @invariant レジスタ中のモード以外のフラグは変更しない。
 */
inline uint8_t
change_mode(const unsigned int next_mode) {
    const uint8_t previous_register = read_mode_register();
    const uint8_t previous_mode = previous_register & MODE_MASK;
    if (next_mode != previous_mode) {
        const uint8_t next_register = (previous_register & ~MODE_MASK) | uint8_t(next_mode);
        write_mode_register(next_register);
    }
    return previous_mode;
}

/**
 * 1byte read
 * @param[in] address アドレス 24bit
 * @return データ
 */
uint8_t
read_byte(const uint32_t address) {
    const uint8_t address_high = (address >> (8 * 2)) & 0xFFu;
    const uint8_t address_mid  = (address >> (8 * 1)) & 0xFFu;
    const uint8_t address_low  = (address >> (8 * 0)) & 0xFFu;
    
    assert_CS();
    spi.write(READ);
    spi.write(address_high);
    spi.write(address_mid);
    spi.write(address_low);
    const uint8_t data = spi.write(0);
    negate_CS();

    return data;
}

/**
 * 1byte write
 * @param[in] address アドレス 24bit
 * @param[in] data データ
 */
void
write_byte(const uint32_t address, const uint8_t data) {
    const uint8_t address_high = (address >> (8 * 2)) & 0xFFu;
    const uint8_t address_mid  = (address >> (8 * 1)) & 0xFFu;
    const uint8_t address_low  = (address >> (8 * 0)) & 0xFFu;

    assert_CS();
    spi.write(WRITE);
    spi.write(address_high);
    spi.write(address_mid);
    spi.write(address_low);
    spi.write(data);
    negate_CS();
}

/**
 * 連続読み出し
 * @pre sequentialモードかpageモードに切り替えていること。
 */
void
read_bytes(const uint32_t address, uint8_t __restrict data[], const uint32_t size) {
    const uint8_t address_high = (address >> (8 * 2)) & 0xFFu;
    const uint8_t address_mid  = (address >> (8 * 1)) & 0xFFu;
    const uint8_t address_low  = (address >> (8 * 0)) & 0xFFu;

    assert_CS();
    spi.write(READ);
    spi.write(address_high);
    spi.write(address_mid);
    spi.write(address_low);
    for (uint32_t i = 0; i < size; ++i) {
        data[i] = spi.write(0x00);
    }
    negate_CS();
}

/**
 * 連続書き込み
 * @pre sequentialモードかpageモードに切り替えていること。
 */
void
write_bytes(const uint32_t address, const uint8_t __restrict data[], const uint32_t size) {
    const uint8_t address_high = (address >> (8 * 2)) & 0xFFu;
    const uint8_t address_mid  = (address >> (8 * 1)) & 0xFFu;
    const uint8_t address_low  = (address >> (8 * 0)) & 0xFFu;

    assert_CS();
    spi.write(WRITE);
    spi.write(address_high);
    spi.write(address_mid);
    spi.write(address_low);
    for (uint32_t i = 0; i < size; ++i) {
        spi.write(data[i]);
    }
    negate_CS();
}

/*
 *
 */


/** 動作確認用 SPI buffer */
uint8_t buf[256] = {};

/**
 * byteアクセスを複数回実行して、照合する
 */
void
try_byte_access() {
    const uint8_t size = 16;
    
    // write data
    for (int i = 0; i < size; i++) {
        write_byte(i, i);
    }

    // read data
    for (int i = 0; i < size; i++) {
        buf[i] = read_byte(i);
    }

    // show data (to SERIAL)
    pc.printf("byte access test: result\r\n");
    for (int i = 0; i < size; i++) {
        pc.printf("  %04x : %02x %s\r\n", i, buf[i], (buf[i] ==i)?"OK":"BAD");
    }
}

/**
 * pageアクセスを実行して、照合する
 */
void
try_page_access() {
    const uint32_t address = 0x1000;
    const uint32_t page_size = 32;

    // write
    for (uint32_t i = 0; i < page_size; ++i) {
        buf[i] = i;
    }
    write_bytes(address, buf, page_size);

    // read
    for (uint32_t i = 0; i < page_size; ++i) {
        buf[i] = 0;
    }

    read_bytes(address, buf, page_size);

    // show data (to SERIAL)
    pc.printf("page access test: result\r\n");
    for (int i = 0; i < page_size; i++) {
        pc.printf("  %04x : %02x %s\r\n", address + i, buf[i], (buf[i] ==i)?"OK":"BAD");
    }
}

/**
 * sequentialアクセスを実行して、照合する
 */
void
try_sequential_access() {
    const uint32_t address = 0x2000;
    const uint32_t size = 256;

    // write
    for (uint32_t i = 0; i < size; ++i) {
        buf[i] = i;
    }

    write_bytes(address, buf, size);

    // read
    for (uint32_t i = 0; i < size; ++i) {
        buf[i] = 0;
    }

    read_bytes(address, buf, size);

    // show data (to SERIAL)
    pc.printf("sequential access test: result\r\n");
    for (int i = 0; i < size; i++) {
        pc.printf("  %04x : %02x %s\r\n", address + i, buf[i], (buf[i] ==i)?"OK":"BAD");
    }
}

/**
 * 動作確認用。TODO: テストコードにすること。
 */
int
main()
{
    pc.baud(115200);
    pc.printf("CPU SystemCoreClock is %.2f MHz\r\n", (float)SystemCoreClock/1.0e6f);

    // initialize SPI
    spi.format(8, 0);   // 8bit, mode=0
    spi.frequency(20 * 1000 * 1000);    // max 20MHz
    negate_CS();

    // read mode
    pc.printf("\r\nread mode register\r\n");
    const uint8_t mode = read_mode_register();
    pc.printf("  mode register = %02x\r\n", mode);

    // test
    pc.printf("\r\npush user button to start: try_single_byte_access\r\n");
    wait_until_user_button_pressed();
    try_single_byte_access();

    pc.printf("\r\npush user button to start: try_sequential_access (default)\r\n");
    wait_until_user_button_pressed();
    try_sequential_access();

    pc.printf("\r\npush user button to start: try_byte_access\r\n");
    wait_until_user_button_pressed();
    change_mode(BYTE);
    try_byte_access();

    pc.printf("\r\npush user button to start: try_page_access\r\n");
    wait_until_user_button_pressed();
    change_mode(PAGE);
    try_page_access();

    pc.printf("\r\npush user button to start: try_sequential_access\r\n");
    wait_until_user_button_pressed();
    change_mode(SEQUENTIAL);
    try_sequential_access();

    pc.printf("\r\nTEST END\r\n\r\n");

    for(;;) {
    }
}