feat: add STM32F103RC support for HY27UF084G2M
This commit is contained in:
11
README.md
11
README.md
@@ -1,13 +1,15 @@
|
|||||||
# parallel-nand
|
# parallel-nand
|
||||||
|
|
||||||
Переносимый C99-драйвер асинхронной parallel NAND x8 и окно управления через
|
Переносимый C99-драйвер асинхронной parallel NAND x8 и окно управления через
|
||||||
общее адресное пространство GAS. Первый профиль — Micron `MT29F1G08ABADA`:
|
общее адресное пространство GAS. Есть профили Micron `MT29F1G08ABADA` и
|
||||||
2048+64 байта, 64 страницы в блоке, 1024 блока.
|
Hynix `HY27UF084G2M` (512 MiB, 2048+64 байта, 64 страницы в блоке,
|
||||||
|
4096 блоков, ID `AD DC 80 95`).
|
||||||
|
|
||||||
Подготовлены аппаратные порты:
|
Подготовлены аппаратные порты:
|
||||||
|
|
||||||
- `TMS320F2812` — XINTF Zone 6;
|
- `TMS320F2812` — XINTF Zone 6;
|
||||||
- `STM32F407VET6` — аппаратный FSMC NAND Bank 2;
|
- `STM32F407VET6` — аппаратный FSMC NAND Bank 2;
|
||||||
|
- `STM32F103RCT6` — GPIO bit-bang, так как в LQFP64 FSMC недоступен;
|
||||||
- `STM32G474CEU6` — GPIO bit-bang для корпуса UFQFPN-48.
|
- `STM32G474CEU6` — GPIO bit-bang для корпуса UFQFPN-48.
|
||||||
|
|
||||||
Ядро не зависит от HAL и транспорта GUI. STM32 HAL используется только в
|
Ядро не зависит от HAL и транспорта GUI. STM32 HAL используется только в
|
||||||
@@ -55,6 +57,11 @@ void memory_init(void)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Для Hynix на STM32F103RCT6 используйте
|
||||||
|
`parallel_nand_port_stm32f103rc()` и
|
||||||
|
`parallel_nand_hy27uf084g2m_geometry`; распиновка и ограничения приведены в
|
||||||
|
[`ports/stm32f103rc/README.md`](ports/stm32f103rc/README.md).
|
||||||
|
|
||||||
Диспетчер общего адресного пространства вызывает
|
Диспетчер общего адресного пространства вызывает
|
||||||
`parallel_nand_gas_read()`/`parallel_nand_gas_write()` для адресов
|
`parallel_nand_gas_read()`/`parallel_nand_gas_write()` для адресов
|
||||||
`0xE000..0xE51F`. Для STM32 с `c/set-protocol/pcan_gas` дополнительно включить
|
`0xE000..0xE51F`. Для STM32 с `c/set-protocol/pcan_gas` дополнительно включить
|
||||||
|
|||||||
@@ -16,6 +16,19 @@ extern "C" {
|
|||||||
#define PNAND_MT29F1G08_PAGE_TOTAL \
|
#define PNAND_MT29F1G08_PAGE_TOTAL \
|
||||||
(PNAND_MT29F1G08_PAGE_MAIN + PNAND_MT29F1G08_PAGE_OOB)
|
(PNAND_MT29F1G08_PAGE_MAIN + PNAND_MT29F1G08_PAGE_OOB)
|
||||||
|
|
||||||
|
#define PNAND_HY27UF084G2M_PAGE_MAIN 2048UL
|
||||||
|
#define PNAND_HY27UF084G2M_PAGE_OOB 64UL
|
||||||
|
#define PNAND_HY27UF084G2M_PAGES_PER_BLOCK 64UL
|
||||||
|
#define PNAND_HY27UF084G2M_BLOCKS 4096UL
|
||||||
|
#define PNAND_HY27UF084G2M_COLUMN_CYCLES 2U
|
||||||
|
#define PNAND_HY27UF084G2M_ROW_CYCLES 3U
|
||||||
|
#define PNAND_HY27UF084G2M_MANUFACTURER_ID 0xADU
|
||||||
|
#define PNAND_HY27UF084G2M_DEVICE_ID 0xDCU
|
||||||
|
#define PNAND_HY27UF084G2M_ID3 0x80U
|
||||||
|
#define PNAND_HY27UF084G2M_ID4 0x95U
|
||||||
|
#define PNAND_HY27UF084G2M_PAGE_TOTAL \
|
||||||
|
(PNAND_HY27UF084G2M_PAGE_MAIN + PNAND_HY27UF084G2M_PAGE_OOB)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PNAND_OK = 0,
|
PNAND_OK = 0,
|
||||||
PNAND_ERROR_ARGUMENT = -1,
|
PNAND_ERROR_ARGUMENT = -1,
|
||||||
@@ -41,6 +54,7 @@ typedef struct {
|
|||||||
} parallel_nand_t;
|
} parallel_nand_t;
|
||||||
|
|
||||||
extern const parallel_nand_geometry_t parallel_nand_mt29f1g08_geometry;
|
extern const parallel_nand_geometry_t parallel_nand_mt29f1g08_geometry;
|
||||||
|
extern const parallel_nand_geometry_t parallel_nand_hy27uf084g2m_geometry;
|
||||||
|
|
||||||
int parallel_nand_init(parallel_nand_t *nand,
|
int parallel_nand_init(parallel_nand_t *nand,
|
||||||
const parallel_nand_port_t *port,
|
const parallel_nand_port_t *port,
|
||||||
|
|||||||
51
ports/stm32f103rc/README.md
Normal file
51
ports/stm32f103rc/README.md
Normal 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(),
|
||||||
|
¶llel_nand_hy27uf084g2m_geometry);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
После успешной инициализации прочитайте четыре байта ID и ожидайте
|
||||||
|
`AD DC 80 95`. Ядро библиотеки работает только на чтение и удерживает `WP#`
|
||||||
|
в активном низком уровне; PROGRAM/ERASE пока намеренно не реализованы.
|
||||||
142
ports/stm32f103rc/parallel_nand_port_stm32f103rc.c
Normal file
142
ports/stm32f103rc/parallel_nand_port_stm32f103rc.c
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#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
|
||||||
|
};
|
||||||
|
|
||||||
|
const parallel_nand_port_t *parallel_nand_port_stm32f103rc(void)
|
||||||
|
{
|
||||||
|
return &f103rc_port;
|
||||||
|
}
|
||||||
16
ports/stm32f103rc/parallel_nand_port_stm32f103rc.h
Normal file
16
ports/stm32f103rc/parallel_nand_port_stm32f103rc.h
Normal 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
|
||||||
@@ -16,6 +16,16 @@ const parallel_nand_geometry_t parallel_nand_mt29f1g08_geometry = {
|
|||||||
1000000UL
|
1000000UL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parallel_nand_geometry_t parallel_nand_hy27uf084g2m_geometry = {
|
||||||
|
PNAND_HY27UF084G2M_PAGE_MAIN,
|
||||||
|
PNAND_HY27UF084G2M_PAGE_OOB,
|
||||||
|
PNAND_HY27UF084G2M_PAGES_PER_BLOCK,
|
||||||
|
PNAND_HY27UF084G2M_BLOCKS,
|
||||||
|
PNAND_HY27UF084G2M_COLUMN_CYCLES,
|
||||||
|
PNAND_HY27UF084G2M_ROW_CYCLES,
|
||||||
|
1000000UL
|
||||||
|
};
|
||||||
|
|
||||||
static int port_valid(const parallel_nand_port_t *port)
|
static int port_valid(const parallel_nand_port_t *port)
|
||||||
{
|
{
|
||||||
return port != 0 && port->init != 0 && port->select != 0 &&
|
return port != 0 && port->init != 0 && port->select != 0 &&
|
||||||
|
|||||||
@@ -76,6 +76,26 @@ int main(void)
|
|||||||
assert(events[5].kind == 'A' && events[5].value == 0x12U);
|
assert(events[5].kind == 'A' && events[5].value == 0x12U);
|
||||||
assert(events[6].kind == 'C' && events[6].value == 0x30U);
|
assert(events[6].kind == 'C' && events[6].value == 0x30U);
|
||||||
|
|
||||||
|
assert(parallel_nand_init(&nand, &fake_port,
|
||||||
|
¶llel_nand_hy27uf084g2m_geometry) == PNAND_OK);
|
||||||
|
assert(parallel_nand_page_count(&nand) == 262144UL);
|
||||||
|
assert(PNAND_HY27UF084G2M_MANUFACTURER_ID == 0xADU);
|
||||||
|
assert(PNAND_HY27UF084G2M_DEVICE_ID == 0xDCU);
|
||||||
|
|
||||||
|
clear_events();
|
||||||
|
input_data[0] = 0xABU;
|
||||||
|
input_count = 1U;
|
||||||
|
input_pos = 0U;
|
||||||
|
assert(parallel_nand_read_page_slice(&nand, 0x23456UL, 0x78UL,
|
||||||
|
bytes, 1UL) == PNAND_OK);
|
||||||
|
assert(bytes[0] == 0xABU);
|
||||||
|
assert(events[2].kind == 'A' && events[2].value == 0x78U);
|
||||||
|
assert(events[3].kind == 'A' && events[3].value == 0x00U);
|
||||||
|
assert(events[4].kind == 'A' && events[4].value == 0x56U);
|
||||||
|
assert(events[5].kind == 'A' && events[5].value == 0x34U);
|
||||||
|
assert(events[6].kind == 'A' && events[6].value == 0x02U);
|
||||||
|
assert(events[7].kind == 'C' && events[7].value == 0x30U);
|
||||||
|
|
||||||
parallel_nand_gas_init(&gas, &nand, PNAND_GAS_DEFAULT_BASE);
|
parallel_nand_gas_init(&gas, &nand, PNAND_GAS_DEFAULT_BASE);
|
||||||
assert(parallel_nand_gas_read(&gas, 0xE000U, ®) == PNAND_GAS_OK);
|
assert(parallel_nand_gas_read(&gas, 0xE000U, ®) == PNAND_GAS_OK);
|
||||||
assert(reg == 0x4E44U);
|
assert(reg == 0x4E44U);
|
||||||
|
|||||||
Reference in New Issue
Block a user