Сохранить текущие изменения NAND и LED перед объединением веток

This commit is contained in:
2026-09-15 15:57:06 +03:00
parent 20dbc2c721
commit 698632ebf3
35 changed files with 1841 additions and 31 deletions

View File

@@ -0,0 +1,51 @@
# Порт STM32F103RCT6 для HY27UF084G2M
У `STM32F103RCT6` корпус LQFP64. Согласно datasheet STM32F103xC/D/E, функция
FSMC в этом корпусе недоступна, поэтому порт выполняет асинхронные NAND-циклы
через GPIO.
## Соединения
| HY27UF084G2M | STM32F103RCT6 | LQFP64 |
|---|---|---:|
| `I/O0..I/O7` | `PB0..PB7` | 26, 27, 28, 55, 56, 57, 58, 59 |
| `CLE` | `PC0` | 8 |
| `ALE` | `PC1` | 9 |
| `WE#` | `PC2` | 10 |
| `RE#` | `PC3` | 11 |
| `CE#` | `PC4` | 24 |
| `WP#` | `PC5` | 25 |
| `R/B#` | `PC6` | 37 |
Питание NAND — 3,3 В. На каждый вывод питания нужен 100 нФ, рядом с чипом —
1–4,7 мкФ. `R/B#` имеет открытый сток и требует внешней подтяжки 4,7–10 кОм
к 3,3 В. Не оставляйте `WP#` неподключённым.
`PB2` также является `BOOT1`: NAND держит I/O в высокоимпедансном состоянии
до командного цикла, но штатную конфигурацию загрузки платы всё равно нужно
обеспечить подтяжкой. `PB3/PB4` заняты JTAG; порт отключает JTAG и сохраняет
SWD на `PA13/PA14`.
## Подключение к проекту Cube/HAL
Добавьте `parallel_nand_port_stm32f103rc.c`, путь к его заголовку и включите
GPIO HAL. Инициализация:
```c
#include "parallel_nand.h"
#include "parallel_nand_port_stm32f103rc.h"
static parallel_nand_t nand;
int nand_init(void)
{
return parallel_nand_init(
&nand,
parallel_nand_port_stm32f103rc(),
&parallel_nand_hy27uf084g2m_geometry);
}
```
После успешной инициализации прочитайте четыре байта ID и ожидайте
`AD DC 80 95`. Порт поддерживает PROGRAM/ERASE: ядро держит `WP#` в активном
низком уровне между операциями и поднимает его только на время изменения NAND.

View File

@@ -0,0 +1,143 @@
#include "parallel_nand_port_stm32f103rc.h"
#include "stm32f1xx_hal.h"
/* STM32F103RCT6 LQFP64: FSMC is unavailable, so use GPIO bit-bang. */
#define DATA_MASK 0x00FFU
#define CLE_PIN GPIO_PIN_0
#define ALE_PIN GPIO_PIN_1
#define WE_PIN GPIO_PIN_2
#define RE_PIN GPIO_PIN_3
#define CE_PIN GPIO_PIN_4
#define WP_PIN GPIO_PIN_5
#define RB_PIN GPIO_PIN_6
static int data_is_output;
static void data_input(void)
{
if (data_is_output) {
GPIOB->CRL = 0x44444444UL; /* PB0..PB7: floating input. */
data_is_output = 0;
}
}
static void data_output(void)
{
if (!data_is_output) {
GPIOB->CRL = 0x33333333UL; /* PB0..PB7: 50 MHz push-pull. */
data_is_output = 1;
}
}
static void short_delay(void)
{
__NOP(); __NOP(); __NOP(); __NOP();
}
static int f103rc_init(void)
{
GPIO_InitTypeDef io;
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/* Latch inactive control levels while pins are still inputs. */
GPIOC->BSRR = WE_PIN | RE_PIN | CE_PIN |
((uint32_t)(CLE_PIN | ALE_PIN | WP_PIN) << 16);
/* PB3/PB4 carry I/O3/I/O4. Keep SWD, release only the JTAG pins. */
__HAL_AFIO_REMAP_SWJ_NOJTAG();
GPIOB->BSRR = DATA_MASK;
GPIOB->CRL = 0x33333333UL;
data_is_output = 1;
data_input();
io.Pin = CLE_PIN | ALE_PIN | WE_PIN | RE_PIN | CE_PIN | WP_PIN;
io.Mode = GPIO_MODE_OUTPUT_PP;
io.Pull = GPIO_NOPULL;
io.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &io);
io.Pin = RB_PIN;
io.Mode = GPIO_MODE_INPUT;
io.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &io);
return 0;
}
static void f103rc_select(void) { GPIOC->BRR = CE_PIN; }
static void f103rc_deselect(void) { GPIOC->BSRR = CE_PIN; }
static void write_cycle(pnand_octet_t value)
{
data_output();
GPIOB->BRR = DATA_MASK;
GPIOB->BSRR = (uint32_t)value & DATA_MASK;
short_delay();
GPIOC->BRR = WE_PIN;
short_delay();
GPIOC->BSRR = WE_PIN;
short_delay();
}
static void f103rc_write_command(pnand_octet_t value)
{
GPIOC->BSRR = CLE_PIN;
GPIOC->BRR = ALE_PIN;
write_cycle(value);
GPIOC->BRR = CLE_PIN;
}
static void f103rc_write_address(pnand_octet_t value)
{
GPIOC->BSRR = ALE_PIN;
GPIOC->BRR = CLE_PIN;
write_cycle(value);
GPIOC->BRR = ALE_PIN;
}
static void f103rc_write_data(pnand_octet_t value)
{
GPIOC->BRR = CLE_PIN | ALE_PIN;
write_cycle(value);
}
static pnand_octet_t f103rc_read_data(void)
{
pnand_octet_t value;
data_input();
GPIOC->BRR = CLE_PIN | ALE_PIN | RE_PIN;
short_delay();
value = (pnand_octet_t)(GPIOB->IDR & DATA_MASK);
GPIOC->BSRR = RE_PIN;
short_delay();
return value;
}
static int f103rc_is_ready(void) { return (GPIOC->IDR & RB_PIN) != 0U; }
static void f103rc_set_wp(int enabled)
{
if (enabled) GPIOC->BRR = WP_PIN;
else GPIOC->BSRR = WP_PIN;
}
static void f103rc_delay_us(unsigned long us)
{
uint32_t cycles = (SystemCoreClock / 3000000UL) * (uint32_t)us;
while (cycles-- != 0U) __NOP();
}
static const parallel_nand_port_t f103rc_port = {
f103rc_init, f103rc_select, f103rc_deselect,
f103rc_write_command, f103rc_write_address, f103rc_write_data,
f103rc_read_data, f103rc_is_ready, f103rc_set_wp, f103rc_delay_us,
1
};
const parallel_nand_port_t *parallel_nand_port_stm32f103rc(void)
{
return &f103rc_port;
}

View File

@@ -0,0 +1,16 @@
#ifndef PARALLEL_NAND_PORT_STM32F103RC_H
#define PARALLEL_NAND_PORT_STM32F103RC_H
#include "parallel_nand_port.h"
#ifdef __cplusplus
extern "C" {
#endif
const parallel_nand_port_t *parallel_nand_port_stm32f103rc(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,62 @@
# Порт STM32F103ZET6 для HY27UF084G2M
Порт использует аппаратный контроллер `FSMC NAND Bank 3`, 8-битную шину и
окно памяти `0x70000000`. Сигналы `CE#`, `WE#`, `RE#`, `CLE` и `ALE`
формируются FSMC. Защита записи `WP#` управляется отдельным GPIO `PG8`.
## Соединения
| HY27UF084G2M | STM32F103ZET6 | LQFP144 | Функция FSMC |
|---|---|---:|---|
| `I/O0` | `PD14` | 85 | `D0` |
| `I/O1` | `PD15` | 86 | `D1` |
| `I/O2` | `PD0` | 114 | `D2` |
| `I/O3` | `PD1` | 115 | `D3` |
| `I/O4` | `PE7` | 58 | `D4` |
| `I/O5` | `PE8` | 59 | `D5` |
| `I/O6` | `PE9` | 60 | `D6` |
| `I/O7` | `PE10` | 63 | `D7` |
| `CLE` | `PD11` | 80 | `A16/CLE` |
| `ALE` | `PD12` | 81 | `A17/ALE` |
| `WE#` | `PD5` | 119 | `NWE` |
| `RE#` | `PD4` | 118 | `NOE` |
| `CE#` | `PD7` | 123 | `NCE2` |
| `R/B#` | `PD6` | 122 | вход GPIO (`NWAIT`) |
| `WP#` | `PG8` | 93 | выход GPIO |
`R/B#` имеет открытый сток и требует внешней подтяжки 4,7–10 кОм к 3,3 В.
Питание NAND — 3,3 В; возле каждой пары питания нужен 100 нФ, рядом с
микросхемой — 1–4,7 мкФ. Все номера выводов MCU указаны для вида сверху.
## Подключение к STM32Cube HAL
В `stm32f1xx_hal_conf.h` должны быть включены `HAL_NAND_MODULE_ENABLED` и
`HAL_GPIO_MODULE_ENABLED`. В проект добавляются:
- `parallel_nand_port_stm32f103ze.c`;
- `stm32f1xx_hal_nand.c`;
- `stm32f1xx_ll_fsmc.c`.
Инициализация переносимой библиотеки:
```c
#include "parallel_nand.h"
#include "parallel_nand_port_stm32f103ze.h"
static parallel_nand_t nand;
int nand_init(void)
{
return parallel_nand_init(
&nand,
parallel_nand_port_stm32f103ze(),
&parallel_nand_hy27uf084g2m_geometry);
}
```
Начальные тайминги намеренно консервативны. После запуска проверьте
осциллографом длительности `WE#`/`RE#` и время установки данных. Ожидаемый ID
HY27UF084G2M: `AD DC 80 95`.
Порт поддерживает PROGRAM/ERASE. `WP#` на `PG8` остаётся активным между
операциями и снимается ядром только на время программирования или стирания.

View File

@@ -0,0 +1,184 @@
#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,
1
};
const parallel_nand_port_t *parallel_nand_port_stm32f103ze(void)
{
return &f103ze_port;
}

View File

@@ -0,0 +1,16 @@
#ifndef PARALLEL_NAND_PORT_STM32F103ZE_H
#define PARALLEL_NAND_PORT_STM32F103ZE_H
#include "parallel_nand_port.h"
#ifdef __cplusplus
extern "C" {
#endif
const parallel_nand_port_t *parallel_nand_port_stm32f103ze(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -24,3 +24,8 @@
`stm32f4xx_ll_fsmc.c`, включить FSMC и не создавать второй
`NAND_HandleTypeDef` для Bank 2. Начальные тайминги
консервативны; финальные значения подтвердить осциллографом.
В базовой схеме `WP#` аппаратно зафиксирован в активном уровне, поэтому порт
объявляет `write_supported = 0`, а PROGRAM/ERASE возвращают
`PNAND_ERROR_WRITE_PROTECTED`. Для записи назначьте отдельный GPIO с внешним
pull-down, реализуйте управление `WP#` и включите признак поддержки порта.

View File

@@ -84,7 +84,8 @@ static void f407_delay_us(unsigned long us)
static const parallel_nand_port_t f407_port = {
f407_init, f407_select, f407_deselect,
f407_write_command, f407_write_address, f407_write_data,
f407_read_data, f407_is_ready, f407_set_wp, f407_delay_us
f407_read_data, f407_is_ready, f407_set_wp, f407_delay_us,
0
};
const parallel_nand_port_t *parallel_nand_port_stm32f407ve(void)

View File

@@ -19,3 +19,6 @@ HAL только для инициализации, а циклы NAND выпо
Выбранные `PB3/PB4` нельзя одновременно использовать для trace/JTAG; SWD на
`PA13/PA14` остаётся доступен. Если эти пины нужны проекту, измените таблицу и
операции шины: текущая оптимизация рассчитана на непрерывные `PB0..PB7`.
Порт поддерживает PROGRAM/ERASE через управляемый `WP#` на `PA5`. Внешний
pull-down сохраняет защиту до инициализации GPIO и при сбросе МК.

View File

@@ -125,7 +125,8 @@ static void g474_delay_us(unsigned long us)
static const parallel_nand_port_t g474_port = {
g474_init, g474_select, g474_deselect,
g474_write_command, g474_write_address, g474_write_data,
g474_read_data, g474_is_ready, g474_set_wp, g474_delay_us
g474_read_data, g474_is_ready, g474_set_wp, g474_delay_us,
1
};
const parallel_nand_port_t *parallel_nand_port_stm32g474ce(void)

View File

@@ -13,9 +13,10 @@
Адреса C28x задаются в 16-битных словах: data `0x100000`, command
`0x100001`, address `0x100002`. Используется только младший байт XINTF.
`WP#` в базовом варианте аппаратный: для режима только чтения соединить с GND
через 10 кОм; если позже понадобится PROGRAM/ERASE, заменить на GPIO и реализовать
`tms_set_wp()`.
`WP#` в базовом варианте аппаратный и соединён с GND через 10 кОм. Поэтому
порт объявляет `write_supported = 0`, а PROGRAM/ERASE возвращают
`PNAND_ERROR_WRITE_PROTECTED`. Для записи нужно перенести `WP#` на GPIO,
реализовать `tms_set_wp()` и установить признак поддержки в структуре порта.
Задержка в порте приблизительная. После выбора частоты CPU проверить импульсы
осциллографом и при необходимости изменить `PNAND_TMS_CPU_MHZ`.

View File

@@ -56,7 +56,8 @@ static void tms_set_wp(int enabled)
static const parallel_nand_port_t tms_port = {
tms_init, tms_select, tms_deselect,
tms_write_command, tms_write_address, tms_write_data,
tms_read_data, tms_is_ready, tms_set_wp, tms_delay_us
tms_read_data, tms_is_ready, tms_set_wp, tms_delay_us,
0
};
const parallel_nand_port_t *parallel_nand_port_tms320f2812(void)