Longan nano を使ってみた [4] – SPI で EEPROM を接続

前回は Longan nano の I2C を動かしてみました.今回は SPI を使って外部デバイスとの接続を試してみたいと思います.

SPI で EEPROM を接続

使用するデバイスは、以前に Raspberry Pi Pico との接続を試した ROHM 社製 EEPROM BR25L320 デバイスです. (下の写真はピッチ変換基板に実装した状態です)
BR25L320
ハードウェア接続
Longan Nano と EEPROM 間の接続は、SPI の 4端子(CS / SCLK / MOSI / MISO) と電源・GND 端子、書込み制御用の WP, HOLD の計 8本になります.SPI は Longan Nano の B12 ~ B15 端子(SPI1)を利用し、 WP, HOLD は GPIO A11 と A12 を接続しています.
ソフトウェア
Platform IO IDE で作成したプロジェクトの main.c に SPI 周りの関数を追加します.
/* SPI 初期設定 */
void longan_spi_eeprom_init()
{
    int ret = 0;
    uint8_t buf[20];

    /******** WP, HOLD pin setting ********/
    rcu_periph_clock_enable(RCU_GPIOA);
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

    GPIO_BC(GPIOA) = GPIO_PIN_11;   // WP (GPIO) = L
    GPIO_BC(GPIOA) = GPIO_PIN_12;   // HOLD (GPIO) = L

    /******** SPI1 pin setting ********/
    rcu_periph_clock_enable( RCU_GPIOB );   // GPIOB clock
    rcu_periph_clock_enable( RCU_AF );      // Alternate Functions clock
    rcu_periph_clock_enable( RCU_SPI1 );    // SPI1 clock

    // SPI1 GPIO pin configuration
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12); 
    gpio_init(GPIOB, GPIO_MODE_AF_PP,  GPIO_OSPEED_50MHZ, GPIO_PIN_13);         
    gpio_init(GPIOB, GPIO_MODE_AF_PP,  GPIO_OSPEED_50MHZ, GPIO_PIN_14);     
    gpio_init(GPIOB, GPIO_MODE_AF_PP,  GPIO_OSPEED_50MHZ, GPIO_PIN_15); 
    
    GPIO_BOP(GPIOB) = GPIO_PIN_12;    // CS (GPIO) = H

    spi_parameter_struct spi_conf = {
        .trans_mode           = SPI_TRANSMODE_FULLDUPLEX,
        .device_mode          = SPI_MASTER,
        .frame_size           = SPI_FRAMESIZE_8BIT,
        .clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE, // MODE0
        .nss                  = SPI_NSS_SOFT,
        .prescale             = SPI_PSC_128, // ABP2=100MHz -> SPI0 ~0.8MHz
        .endian               = SPI_ENDIAN_MSB
    };
    spi_init(SPI1, &spi_conf);
    spi_enable(SPI1);

    if (spi_i2s_flag_get(SPI1, SPI_FLAG_CONFERR) == SET) {
        snprintf(buf, 50, "Init error : %d", ret);
        LCD_ShowString( 5, 5, buf, GREEN );
    }
    else {
        snprintf(buf, 50, "Init OK : %d", ret);
        LCD_ShowString( 5, 5, buf, GREEN );
    }
}

/* WP 端子制御 */
void spi_eep_wp_low()
{
    GPIO_BC(GPIOA) = GPIO_PIN_11;    // WP (GPIO) = L
}

void spi_eep_wp_high()
{
    GPIO_BOP(GPIOA) = GPIO_PIN_11;    // WP (GPIO) = H
}

/* HOLD 端子制御 */
void spi_eep_hold_low()
{
    GPIO_BC(GPIOA) = GPIO_PIN_12;    // HOLD (GPIO) = L
}

void spi_eep_hold_high()
{
    GPIO_BOP(GPIOA) = GPIO_PIN_12;    // HOLD (GPIO) = H
}

/* CS 端子制御 */
void spi_eep_cs_low()
{
    GPIO_BC(GPIOB) = GPIO_PIN_12;    // CS (GPIO) = L
}

void spi_eep_cs_high()
{
    GPIO_BOP(GPIOB) = GPIO_PIN_12;    // CS (GPIO) = H
}

/* SPI 指定 byte 数データ書き込み */
void longan_spi_write_byte(uint8_t *data, uint8_t length) 
{
    int i;
    spi_eep_cs_low();
    spi_eep_wp_high();
    spi_eep_hold_high();
    delay_1ms(1);

    for( i = 0; i < length; i++ ) {
        while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));
        spi_i2s_data_transmit(SPI1, data[i]);
    }
    delay_1ms(1);

    spi_eep_cs_high();
    spi_eep_wp_low();
    spi_eep_hold_low();
    delay_1ms(1);
}

/* SPI 1byte データ読み出し */
void longan_spi_read_byte(uint8_t *wdata, uint8_t *rdata, uint8_t length) 
{
    int i;
    uint8_t str[20] = {0};
    spi_eep_cs_low();
    spi_eep_wp_high();
    spi_eep_hold_high();
    delay_1ms(1);

    for( i = 0; i < length; i++ ) {
        while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));
        spi_i2s_data_transmit(SPI1, wdata[i]);
    
        while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE));
        rdata[i] = spi_i2s_data_receive(SPI1);
    }
    while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE));
    rdata[i] = spi_i2s_data_receive(SPI1);
    delay_1ms(1);
    
    spi_eep_cs_high();
    spi_eep_wp_low();
    spi_eep_hold_low();
    delay_1ms(1);
}

/* EEPROM データ書き込み */
void longan_spi_eeprom_write(uint16_t addr, uint8_t data)
{
    uint8_t buf[20] = {0};
    uint8_t addr_h = (uint8_t)((addr & 0xFF00) >> 8);
    uint8_t addr_l = (uint8_t)(addr & 0x00FF);

    buf[0] = 0x06;
    longan_spi_write_byte(buf, 1);

    buf[0] = 0x02;
    buf[1] = addr_h;
    buf[2] = addr_l;
    buf[3] = data;
    longan_spi_write_byte(buf, 4);
    
    buf[0] = 0x04;
    longan_spi_write_byte(buf, 1);

    snprintf(buf, 20, "A : %x%x  D : %x", addr_h, addr_l, data);
    LCD_ShowString( 5, 5, buf, GREEN );
}

/* EEPROM データ読み出し */
uint8_t longan_spi_eeprom_read(uint16_t addr)
{
    uint8_t buf[20] = {0};
    uint8_t rbuf[20] = {0};
    uint8_t str[20] = {0};
    uint8_t addr_h = (uint8_t)((addr & 0xFF00) >> 8);
    uint8_t addr_l = (uint8_t)(addr & 0x00FF);

    buf[0] = 0x03;
    buf[1] = addr_h;
    buf[2] = addr_l;
    buf[3] = 0x00;
    longan_spi_read_byte(buf, rbuf, 4);

    snprintf(str, 20, "D : %x %x %x %x %x", rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4]);
    LCD_ShowString( 5, 30, str, GREEN );

    //snprintf(str, 20, "D : %x %x %x %x", rbuf[4], rbuf[5], rbuf[6], rbuf[7]);
    //LCD_ShowString( 5, 55, str, GREEN );

    return rbuf[4];
}
main() 内で定期的に上記関数を呼んで EEPROM にアクセスしデータを書き込み&読み込みを行ない、LCD に表示しています.
int main(void)
{
    u8 data[2];
    u8 buf[12];
    longan_led_init();

    Lcd_Init();             // LCD を初期化
    LCD_Clear( CYAN );      // LCD をクリア
    BACK_COLOR = BLACK;     // 文字の背景色

    longan_spi_eeprom_init();
    spi_eep_wp_low();
    spi_eep_hold_low();
    spi_eep_cs_high();
    delay_1ms(100);

    longan_spi_eeprom_write(0x0012, 0xAA);      /* アドレス 0x0012 にデータ 0xAA を書き込み*/
    data[0] = longan_spi_eeprom_read(0x0012);   /* アドレス 0x0012 を読み出し */

    snprintf(buf, 20, "D = %x", data[0]);
    LCD_ShowString( 5, 55, buf, GREEN );
    while(1) {
        i2c_read(0x48, data, 2);
        snprintf(buf, 12, "Temp = %d.", data[0]);
        LCD_ShowString( 5, 10, buf, GREEN );

        /* turn on builtin led */
        longan_led_on();
        delay_1ms(100);
        /* turn off uiltin led */
        longan_led_off();
        delay_1ms(100);
    }
}
実行結果
実行結果

書き込み時、読み出し時それぞれの波形を示します.
Pulse View 波形(Write Enable コマンド発行)
Pulse View 波形(Write)
Pulse View 波形(Write Disable コマンド発行)
Pulse View 波形(Read)
参考
プログラムの作成にあたっては、以下を参考とさせていただきました.
Programmer All

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です