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