#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "spi_flash.h" void init_spi(void) { /* Set up clocks for SPI 1 */ rcc_clock_setup_in_hsi_out_48mhz(); rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOB); rcc_periph_clock_enable(RCC_SPI1); /* Set up GPIOs for SPI 1 */ gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO4); gpio_set(GPIOA, GPIO4); gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO3 | GPIO4 | GPIO5); gpio_set_af(GPIOB, GPIO_AF0, GPIO3 | GPIO4 | GPIO5); spi_set_master_mode(SPI1); spi_set_baudrate_prescaler(SPI1, SPI_CR1_BR_FPCLK_DIV_64); spi_set_clock_polarity_0(SPI1); spi_set_clock_phase_0(SPI1); spi_set_full_duplex_mode(SPI1); spi_set_unidirectional_mode(SPI1); /* bidirectional but in 3-wire */ spi_set_data_size(SPI1, SPI_CR2_DS_8BIT); spi_enable_software_slave_management(SPI1); spi_send_msb_first(SPI1); spi_set_nss_high(SPI1); spi_fifo_reception_threshold_8bit(SPI1); SPI_I2SCFGR(SPI1) &= ~SPI_I2SCFGR_I2SMOD; spi_enable(SPI1); } void spi_send_cmd(uint32_t dev, const char *cmd, size_t cmd_len, char *data, size_t data_len) { size_t i; gpio_clear(GPIOA, GPIO4); for (i = 0; i < cmd_len; ++i) { spi_send8(dev, *cmd++); (void)spi_read8(dev); } for (i = 0; i < data_len; ++i) { spi_send8(dev, 0); *data++ = spi_read8(dev); } gpio_set(GPIOA, GPIO4); } static void spi_flash_addr(char *cmd, uint32_t addr) { cmd[1] = addr >> 16; cmd[2] = addr >> 8; cmd[3] = addr; } static void spi_enable_write(uint32_t dev) { char cmd[1] = { CMD_WRITE_ENABLE }; spi_send_cmd(dev, cmd, 1, NULL, 0); } static void spi_disable_write(uint32_t dev) { char cmd[1] = { CMD_WRITE_DISABLE }; spi_send_cmd(dev, cmd, 1, NULL, 0); } void spi_flash_read(uint32_t dev, uint32_t addr, char *data, size_t len) { char cmd[4] = { CMD_READ_ARRAY_SLOW }; spi_flash_addr(cmd, addr); spi_send_cmd(dev, cmd, 4, data, len); } void spi_flash_write(uint32_t dev, uint32_t addr, char *data, size_t len) { char cmd[4] = { CMD_PAGE_PROGRAM }; size_t i; spi_enable_write(dev); spi_flash_addr(cmd, addr); gpio_clear(GPIOA, GPIO4); for (i = 0; i < 4; ++i) { spi_send8(dev, cmd[i]); (void)spi_read8(dev); } for (i = 0; i < len; ++i) { spi_send8(dev, data[i]); (void)spi_read8(dev); } gpio_set(GPIOA, GPIO4); spi_disable_write(dev); } void spi_flash_erase(FILE *fp, uint32_t dev, uint32_t addr, size_t len) { char cmd[4]; size_t size; spi_enable_write(dev); addr = ROUND_UP(addr, 4 * KIB); for (; len; addr += size, len -= size) { if (IS_ROUND(addr, 64 * KIB) && IS_ROUND(len, 64 * KIB)) size = 64 * KIB; else if (IS_ROUND(addr, 32 * KIB) && IS_ROUND(len, 32 * KIB)) size = 32 * KIB; else if (IS_ROUND(addr, 4 * KIB) && IS_ROUND(len, 4 * KIB)) size = 4 * KIB; else size = 0; switch (size) { case 64 * KIB: cmd[0] = CMD_ERASE_64K; break; case 32 * KIB: cmd[0] = CMD_ERASE_32K; break; case 4 * KIB: cmd[0] = CMD_ERASE_4K; break; default: goto err_disable_write; }; fprintf(fp, "addr: %lx; size: %u\n", addr, size); spi_flash_addr(cmd, addr); spi_send_cmd(dev, cmd, 4, NULL, 0); } err_disable_write: spi_disable_write(dev); }