STM32 HALライブラリによるW25Q128フラッシュメモリのSPI通信実装

SPI(Serial Peripheral Interface)はマイコンと周辺デバイス間の高速データ伝送を実現する代表的なシリアル通信プロトコルです。128Mbit(16MB)の容量を持つW25Q128フラッシュメモリは、データ記録やプログラム格納用途に広く利用されています。STM32F407はARM Cortex-M4コアを搭載し、ハードウェアSPIインターフェースをサポートする他、ソフトウェアエミュレーションによるSPI通信も実現可能です。

マイコンピンW25Q128ピン機能
PA5(CLK)CLKSPIクロック信号
PA6(MISO)DO主装置入力/従装置出力
PA7(MOSI)DI主装置出力/従装置入力
PA4(CS)CSチップセレクト信号

ソフトウェアエミュレーションSPI実装

GPIO初期化

void init_spi_gpio(void) {
    GPIO_InitTypeDef gpio_init = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();

    gpio_init.Pin = GPIO_PIN_5 | GPIO_PIN_7 | GPIO_PIN_4;
    gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init.Pull = GPIO_NOPULL;
    gpio_init.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &gpio_init);

    gpio_init.Pin = GPIO_PIN_6;
    gpio_init.Mode = GPIO_MODE_INPUT;
    HAL_GPIO_Init(GPIOA, &gpio_init);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

ビットレベル通信

void spi_shift_bit(uint8_t bit) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, (GPIO_PinState)bit);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}

uint8_t spi_receive_bit(void) {
    uint8_t bit;
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
    bit = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
    return bit;
}

バイトレベル通信

uint8_t spi_send_byte(uint8_t data) {
    uint8_t received = 0;
    for (uint8_t i = 0; i < 8; i++) {
        spi_shift_bit((data >> (7 - i)) & 0x01);
        received |= (spi_receive_bit() << (7 - i));
    }
    return received;
}

W25Q128操作関数

uint16_t read_w25q128_id(void) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    spi_send_byte(0x90);
    spi_send_byte(0x00);
    spi_send_byte(0x00);
    spi_send_byte(0x00);
    uint16_t id = (uint16_t)spi_send_byte(0xFF) << 8;
    id |= spi_send_byte(0xFF);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    return id;
}

void enable_write(void) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    spi_send_byte(0x06);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

ハードウェアSPI実装

SPI外設初期化

void init_spi1(void) {
    spi_handle.Instance = SPI1;
    spi_handle.Init.Mode = SPI_MODE_MASTER;
    spi_handle.Init.Direction = SPI_DIRECTION_2LINES;
    spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;
    spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;
    spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;
    spi_handle.Init.NSS = SPI_NSS_SOFT;
    spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
    spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
    if (HAL_SPI_Init(&spi_handle) != HAL_OK) {
        Error_Handler();
    }
}

void spi1_msp_init(SPI_HandleTypeDef *hspi) {
    GPIO_InitTypeDef gpio_init = {0};
    if (hspi->Instance == SPI1) {
        __HAL_RCC_SPI1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        gpio_init.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
        gpio_init.Mode = GPIO_MODE_AF_PP;
        gpio_init.Pull = GPIO_NOPULL;
        gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        gpio_init.Alternate = GPIO_AF5_SPI1;
        HAL_GPIO_Init(GPIOA, &gpio_init);
        gpio_init.Pin = GPIO_PIN_4;
        gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
        HAL_GPIO_Init(GPIOA, &gpio_init);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    }
}

W25Q128操作関数(ハードウェアSPI)

uint16_t read_id_hw(void) {
    uint8_t tx[4] = {0x90, 0x00, 0x00, 0x00};
    uint8_t rx[2];
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&spi_handle, tx, 4, HAL_MAX_DELAY);
    HAL_SPI_Receive(&spi_handle, rx, 2, HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    return (uint16_t)rx[0] << 8 | rx[1];
}

void sector_erase_hw(uint32_t addr) {
    enable_write_hw();
    uint8_t tx[4] = {0x20, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr};
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&spi_handle, tx, 4, HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

テストメイン関数

int main(void) {
    HAL_Init();
    SystemClock_Config();
    init_spi_gpio();
    init_spi1();

    uint16_t id_soft = read_w25q128_id();
    uint16_t id_hard = read_id_hw();

    uint8_t write_buf[] = {0x11, 0x22, 0x33, 0x44};
    uint8_t read_buf[4];

    sector_erase(0x000000);
    spi_page_program(0x000000, write_buf, 4);
    spi_read_data(0x000000, read_buf, 4);

    sector_erase_hw(0x000000);
    spi_page_program_hw(0x000000, write_buf, 4);
    spi_read_data_hw(0x000000, read_buf, 4);

    while (1);
}

タグ: STM32 w25q128 HAL SPI embedded

6月30日 20:43 投稿