#include "parallel_nand_port_stm32f103ze.h" #include "stm32f1xx_hal.h" #include "stm32f1xx_hal_nand.h" /* STM32F103 names this block FSMC NAND Bank 3. Its HAL memory window is * NAND_DEVICE1; each access makes FSMC assert PD7/FSMC_NCE2 automatically. */ #define PNAND_F103ZE_DATA_ADDRESS 0x70000000UL #define PNAND_F103ZE_COMMAND_AREA (1UL << 16U) #define PNAND_F103ZE_ADDRESS_AREA (1UL << 17U) /* WP# is not generated by FSMC, so it is controlled as an ordinary GPIO. */ #define PNAND_F103ZE_WP_PORT GPIOG #define PNAND_F103ZE_WP_PIN GPIO_PIN_8 /* HAL owns the FSMC registers through this handle during initialization. */ static NAND_HandleTypeDef nand_handle; /** Configure all FSMC signals that belong to GPIO port D. */ static void configure_fsmc_gpio_d(void) { GPIO_InitTypeDef pin = {0}; /* D0..D3, NOE, NWE, NCE2, CLE and ALE are driven by FSMC. On STM32F1 * alternate-function output mode is selected without an AF number. */ pin.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_15; pin.Mode = GPIO_MODE_AF_PP; pin.Pull = GPIO_NOPULL; pin.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOD, &pin); /* R/B# is read directly. Its open-drain output needs an external * 4.7..10 kOhm pull-up to 3.3 V. */ pin.Pin = GPIO_PIN_6; pin.Mode = GPIO_MODE_INPUT; pin.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOD, &pin); } /** Configure FSMC data bits D4..D7 on GPIO port E. */ static void configure_fsmc_gpio_e(void) { GPIO_InitTypeDef pin = {0}; pin.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10; pin.Mode = GPIO_MODE_AF_PP; pin.Pull = GPIO_NOPULL; pin.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOE, &pin); } /** Configure the explicit write-protect output and assert protection. */ static void configure_write_protect(void) { GPIO_InitTypeDef pin = {0}; /* Set the safe active-low level before changing the pad to an output. */ HAL_GPIO_WritePin(PNAND_F103ZE_WP_PORT, PNAND_F103ZE_WP_PIN, GPIO_PIN_RESET); pin.Pin = PNAND_F103ZE_WP_PIN; pin.Mode = GPIO_MODE_OUTPUT_PP; pin.Pull = GPIO_NOPULL; pin.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(PNAND_F103ZE_WP_PORT, &pin); } /** Initialize the STM32F1 HAL NAND layer with conservative 72 MHz timings. */ static int f103ze_init(void) { FSMC_NAND_PCC_TimingTypeDef common_timing = {0}; FSMC_NAND_PCC_TimingTypeDef attribute_timing = {0}; __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_FSMC_CLK_ENABLE(); configure_fsmc_gpio_d(); configure_fsmc_gpio_e(); configure_write_protect(); nand_handle.Instance = FSMC_NAND_DEVICE; nand_handle.Init.NandBank = FSMC_NAND_BANK3; nand_handle.Init.Waitfeature = FSMC_NAND_PCC_WAIT_FEATURE_DISABLE; nand_handle.Init.MemoryDataWidth = FSMC_NAND_PCC_MEM_BUS_WIDTH_8; nand_handle.Init.EccComputation = FSMC_NAND_ECC_DISABLE; nand_handle.Init.ECCPageSize = FSMC_NAND_ECC_PAGE_SIZE_2048BYTE; nand_handle.Init.TCLRSetupTime = 2U; nand_handle.Init.TARSetupTime = 2U; /* One FSMC tick is about 13.9 ns at HCLK=72 MHz. These deliberately * relaxed values are suitable for first bring-up and signal probing. */ common_timing.SetupTime = 2U; common_timing.WaitSetupTime = 5U; common_timing.HoldSetupTime = 3U; common_timing.HiZSetupTime = 2U; attribute_timing = common_timing; return HAL_NAND_Init(&nand_handle, &common_timing, &attribute_timing) == HAL_OK ? 0 : -1; } static void f103ze_select(void) { /* A memory-window access asserts NCE2; no separate software GPIO exists. */ } static void f103ze_deselect(void) { /* NCE2 is released automatically when the FSMC access finishes. */ } static void f103ze_write_command(pnand_octet_t value) { *(__IO uint8_t *)(PNAND_F103ZE_DATA_ADDRESS | PNAND_F103ZE_COMMAND_AREA) = (uint8_t)value; __DSB(); /* Complete the external bus write before the next NAND cycle. */ } static void f103ze_write_address(pnand_octet_t value) { *(__IO uint8_t *)(PNAND_F103ZE_DATA_ADDRESS | PNAND_F103ZE_ADDRESS_AREA) = (uint8_t)value; __DSB(); } static void f103ze_write_data(pnand_octet_t value) { *(__IO uint8_t *)PNAND_F103ZE_DATA_ADDRESS = (uint8_t)value; __DSB(); } static pnand_octet_t f103ze_read_data(void) { uint8_t value = *(__IO uint8_t *)PNAND_F103ZE_DATA_ADDRESS; __DSB(); return (pnand_octet_t)value; } static int f103ze_is_ready(void) { return HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_6) == GPIO_PIN_SET; } static void f103ze_set_write_protect(int enabled) { /* NAND WP# is active low: RESET protects, SET permits program/erase. */ HAL_GPIO_WritePin(PNAND_F103ZE_WP_PORT, PNAND_F103ZE_WP_PIN, enabled ? GPIO_PIN_RESET : GPIO_PIN_SET); } static void f103ze_delay_us(unsigned long us) { /* Approximate delay used only around reset/status polling. The factor * accounts for loop instructions in addition to the NOP. */ uint32_t cycles = (SystemCoreClock / 3000000UL) * (uint32_t)us; while (cycles-- != 0U) { __NOP(); } } static const parallel_nand_port_t f103ze_port = { f103ze_init, f103ze_select, f103ze_deselect, f103ze_write_command, f103ze_write_address, f103ze_write_data, f103ze_read_data, f103ze_is_ready, f103ze_set_write_protect, f103ze_delay_us }; const parallel_nand_port_t *parallel_nand_port_stm32f103ze(void) { return &f103ze_port; }