Добавить переносимый драйвер parallel NAND
This commit is contained in:
26
c/parallel-nand/ports/stm32f407ve/README.md
Normal file
26
c/parallel-nand/ports/stm32f407ve/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Порт STM32F407VET6
|
||||
|
||||
Используется аппаратный FSMC NAND Bank 2, шина x8.
|
||||
|
||||
| NAND | STM32F407VE | LQFP-100 |
|
||||
|---|---|---:|
|
||||
| `I/O0` | `PD14/FSMC_D0` | 61 |
|
||||
| `I/O1` | `PD15/FSMC_D1` | 62 |
|
||||
| `I/O2` | `PD0/FSMC_D2` | 81 |
|
||||
| `I/O3` | `PD1/FSMC_D3` | 82 |
|
||||
| `I/O4` | `PE7/FSMC_D4` | 38 |
|
||||
| `I/O5` | `PE8/FSMC_D5` | 39 |
|
||||
| `I/O6` | `PE9/FSMC_D6` | 40 |
|
||||
| `I/O7` | `PE10/FSMC_D7` | 41 |
|
||||
| `RE#` | `PD4/FSMC_NOE` | 85 |
|
||||
| `WE#` | `PD5/FSMC_NWE` | 86 |
|
||||
| `R/B#` | `PD6/FSMC_NWAIT` | 87 |
|
||||
| `CE#` | `PD7/FSMC_NE2` | 88 |
|
||||
| `CLE` | `PD11/FSMC_A16` | 58 |
|
||||
| `ALE` | `PD12/FSMC_A17` | 59 |
|
||||
|
||||
Порт использует STM32Cube HAL. В `stm32f4xx_hal_conf.h` включить
|
||||
`HAL_NAND_MODULE_ENABLED`, добавить `stm32f4xx_hal_nand.c` и
|
||||
`stm32f4xx_ll_fsmc.c`, включить FSMC и не создавать второй
|
||||
`NAND_HandleTypeDef` для Bank 2. Начальные тайминги
|
||||
консервативны; финальные значения подтвердить осциллографом.
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "parallel_nand_port_stm32f407ve.h"
|
||||
#include "stm32f4xx_hal.h"
|
||||
#include "stm32f4xx_hal_nand.h"
|
||||
|
||||
#define PNAND_F407_BANK2_BASE 0x70000000UL
|
||||
#define PNAND_F407_CMD_AREA (1UL << 16)
|
||||
#define PNAND_F407_ADDR_AREA (1UL << 17)
|
||||
|
||||
static NAND_HandleTypeDef hnand;
|
||||
|
||||
static int f407_init(void)
|
||||
{
|
||||
GPIO_InitTypeDef io;
|
||||
FSMC_NAND_PCC_TimingTypeDef common;
|
||||
FSMC_NAND_PCC_TimingTypeDef attribute;
|
||||
|
||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOE_CLK_ENABLE();
|
||||
__HAL_RCC_FSMC_CLK_ENABLE();
|
||||
|
||||
io.Mode = GPIO_MODE_AF_PP;
|
||||
io.Pull = GPIO_NOPULL;
|
||||
io.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
io.Alternate = GPIO_AF12_FSMC;
|
||||
io.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 |
|
||||
GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_11 | GPIO_PIN_12 |
|
||||
GPIO_PIN_14 | GPIO_PIN_15;
|
||||
HAL_GPIO_Init(GPIOD, &io);
|
||||
io.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10;
|
||||
HAL_GPIO_Init(GPIOE, &io);
|
||||
|
||||
hnand.Instance = FSMC_NAND_DEVICE;
|
||||
hnand.Init.NandBank = FSMC_NAND_BANK2;
|
||||
hnand.Init.Waitfeature = FSMC_NAND_PCC_WAIT_FEATURE_DISABLE;
|
||||
hnand.Init.MemoryDataWidth = FSMC_NAND_PCC_MEM_BUS_WIDTH_8;
|
||||
hnand.Init.EccComputation = FSMC_NAND_ECC_DISABLE;
|
||||
hnand.Init.ECCPageSize = FSMC_NAND_ECC_PAGE_SIZE_2048BYTE;
|
||||
hnand.Init.TCLRSetupTime = 2U;
|
||||
hnand.Init.TARSetupTime = 2U;
|
||||
|
||||
common.SetupTime = 2U;
|
||||
common.WaitSetupTime = 5U;
|
||||
common.HoldSetupTime = 3U;
|
||||
common.HiZSetupTime = 2U;
|
||||
attribute = common;
|
||||
return HAL_NAND_Init(&hnand, &common, &attribute) == HAL_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
static void f407_select(void) { /* FSMC_NCE2 активируется доступом. */ }
|
||||
static void f407_deselect(void) { }
|
||||
static void f407_write_command(pnand_octet_t value)
|
||||
{
|
||||
*(__IO uint8_t *)(PNAND_F407_BANK2_BASE | PNAND_F407_CMD_AREA) =
|
||||
(uint8_t)value;
|
||||
}
|
||||
static void f407_write_address(pnand_octet_t value)
|
||||
{
|
||||
*(__IO uint8_t *)(PNAND_F407_BANK2_BASE | PNAND_F407_ADDR_AREA) =
|
||||
(uint8_t)value;
|
||||
}
|
||||
static void f407_write_data(pnand_octet_t value)
|
||||
{
|
||||
*(__IO uint8_t *)PNAND_F407_BANK2_BASE = (uint8_t)value;
|
||||
}
|
||||
static pnand_octet_t f407_read_data(void)
|
||||
{
|
||||
return *(__IO uint8_t *)PNAND_F407_BANK2_BASE;
|
||||
}
|
||||
static int f407_is_ready(void)
|
||||
{
|
||||
return HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_6) == GPIO_PIN_SET;
|
||||
}
|
||||
static void f407_set_wp(int enabled)
|
||||
{
|
||||
/* WP# аппаратный в базовой схеме. */
|
||||
(void)enabled;
|
||||
}
|
||||
static void f407_delay_us(unsigned long us)
|
||||
{
|
||||
uint32_t cycles = (SystemCoreClock / 3000000UL) * (uint32_t)us;
|
||||
while (cycles-- != 0U) __NOP();
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
const parallel_nand_port_t *parallel_nand_port_stm32f407ve(void)
|
||||
{
|
||||
return &f407_port;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef PARALLEL_NAND_PORT_STM32F407VE_H
|
||||
#define PARALLEL_NAND_PORT_STM32F407VE_H
|
||||
|
||||
#include "parallel_nand_port.h"
|
||||
|
||||
const parallel_nand_port_t *parallel_nand_port_stm32f407ve(void);
|
||||
|
||||
#endif
|
||||
21
c/parallel-nand/ports/stm32g474ce/README.md
Normal file
21
c/parallel-nand/ports/stm32g474ce/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Порт STM32G474CEU6
|
||||
|
||||
У UFQFPN-48 нет выведенного FSMC, поэтому используется GPIO bit-bang.
|
||||
|
||||
| NAND | STM32G474CEU6 | UFQFPN-48 |
|
||||
|---|---|---:|
|
||||
| `I/O0..I/O7` | `PB0..PB7` | 17, 18, 19, 41, 42, 43, 44, 45 |
|
||||
| `CLE` | `PA0` | 8 |
|
||||
| `ALE` | `PA1` | 9 |
|
||||
| `WE#` | `PA2` | 10 |
|
||||
| `RE#` | `PA3` | 11 |
|
||||
| `CE#` | `PA4` | 12 |
|
||||
| `WP#` | `PA5` | 13 |
|
||||
| `R/B#` | `PA6` | 14 |
|
||||
|
||||
`R/B#` требует внешней подтяжки 4,7–10 кОм к 3,3 В. Порт использует STM32Cube
|
||||
HAL только для инициализации, а циклы NAND выполняет прямым доступом к GPIO.
|
||||
|
||||
Выбранные `PB3/PB4` нельзя одновременно использовать для trace/JTAG; SWD на
|
||||
`PA13/PA14` остаётся доступен. Если эти пины нужны проекту, измените таблицу и
|
||||
операции шины: текущая оптимизация рассчитана на непрерывные `PB0..PB7`.
|
||||
@@ -0,0 +1,134 @@
|
||||
#include "parallel_nand_port_stm32g474ce.h"
|
||||
#include "stm32g4xx_hal.h"
|
||||
|
||||
/* Пустая плата STM32G474CEU6: PB0..PB7 образуют непрерывную x8 шину. */
|
||||
#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->MODER &= ~0x0000FFFFUL;
|
||||
data_is_output = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void data_output(void)
|
||||
{
|
||||
if (!data_is_output) {
|
||||
GPIOB->MODER = (GPIOB->MODER & ~0x0000FFFFUL) | 0x00005555UL;
|
||||
data_is_output = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void short_delay(void)
|
||||
{
|
||||
__NOP(); __NOP(); __NOP(); __NOP();
|
||||
__NOP(); __NOP(); __NOP(); __NOP();
|
||||
}
|
||||
|
||||
static int g474_init(void)
|
||||
{
|
||||
GPIO_InitTypeDef io;
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
|
||||
io.Pin = DATA_MASK;
|
||||
io.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
io.Pull = GPIO_NOPULL;
|
||||
io.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
HAL_GPIO_Init(GPIOB, &io);
|
||||
data_is_output = 1;
|
||||
data_input();
|
||||
|
||||
/* Set inactive levels before enabling the pins as outputs. */
|
||||
GPIOA->BSRR = WE_PIN | RE_PIN | CE_PIN |
|
||||
((uint32_t)(CLE_PIN | ALE_PIN | WP_PIN) << 16);
|
||||
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_VERY_HIGH;
|
||||
HAL_GPIO_Init(GPIOA, &io);
|
||||
io.Pin = RB_PIN;
|
||||
io.Mode = GPIO_MODE_INPUT;
|
||||
io.Pull = GPIO_NOPULL;
|
||||
HAL_GPIO_Init(GPIOA, &io);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void g474_select(void) { GPIOA->BSRR = (uint32_t)CE_PIN << 16; }
|
||||
static void g474_deselect(void) { GPIOA->BSRR = CE_PIN; }
|
||||
|
||||
static void write_cycle(pnand_octet_t value)
|
||||
{
|
||||
data_output();
|
||||
GPIOB->BSRR = (uint32_t)DATA_MASK << 16;
|
||||
GPIOB->BSRR = (uint32_t)value & DATA_MASK;
|
||||
short_delay();
|
||||
GPIOA->BSRR = (uint32_t)WE_PIN << 16;
|
||||
short_delay();
|
||||
GPIOA->BSRR = WE_PIN;
|
||||
short_delay();
|
||||
}
|
||||
|
||||
static void g474_write_command(pnand_octet_t value)
|
||||
{
|
||||
GPIOA->BSRR = CLE_PIN | ((uint32_t)ALE_PIN << 16);
|
||||
write_cycle(value);
|
||||
GPIOA->BSRR = (uint32_t)CLE_PIN << 16;
|
||||
}
|
||||
|
||||
static void g474_write_address(pnand_octet_t value)
|
||||
{
|
||||
GPIOA->BSRR = ALE_PIN | ((uint32_t)CLE_PIN << 16);
|
||||
write_cycle(value);
|
||||
GPIOA->BSRR = (uint32_t)ALE_PIN << 16;
|
||||
}
|
||||
|
||||
static void g474_write_data(pnand_octet_t value)
|
||||
{
|
||||
GPIOA->BSRR = ((uint32_t)(CLE_PIN | ALE_PIN) << 16);
|
||||
write_cycle(value);
|
||||
}
|
||||
|
||||
static pnand_octet_t g474_read_data(void)
|
||||
{
|
||||
pnand_octet_t value;
|
||||
data_input();
|
||||
GPIOA->BSRR = ((uint32_t)(CLE_PIN | ALE_PIN | RE_PIN) << 16);
|
||||
short_delay();
|
||||
value = (pnand_octet_t)(GPIOB->IDR & DATA_MASK);
|
||||
GPIOA->BSRR = RE_PIN;
|
||||
short_delay();
|
||||
return value;
|
||||
}
|
||||
|
||||
static int g474_is_ready(void) { return (GPIOA->IDR & RB_PIN) != 0U; }
|
||||
static void g474_set_wp(int enabled)
|
||||
{
|
||||
GPIOA->BSRR = enabled ? ((uint32_t)WP_PIN << 16) : WP_PIN;
|
||||
}
|
||||
static void g474_delay_us(unsigned long us)
|
||||
{
|
||||
uint32_t cycles = (SystemCoreClock / 3000000UL) * (uint32_t)us;
|
||||
while (cycles-- != 0U) __NOP();
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
const parallel_nand_port_t *parallel_nand_port_stm32g474ce(void)
|
||||
{
|
||||
return &g474_port;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef PARALLEL_NAND_PORT_STM32G474CE_H
|
||||
#define PARALLEL_NAND_PORT_STM32G474CE_H
|
||||
|
||||
#include "parallel_nand_port.h"
|
||||
|
||||
const parallel_nand_port_t *parallel_nand_port_stm32g474ce(void);
|
||||
|
||||
#endif
|
||||
21
c/parallel-nand/ports/tms320f2812/README.md
Normal file
21
c/parallel-nand/ports/tms320f2812/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Порт TMS320F2812
|
||||
|
||||
Порт использует XINTF Zone 6:
|
||||
|
||||
- `XD0..XD7` — NAND `I/O0..I/O7`;
|
||||
- `XA0` — `CLE`;
|
||||
- `XA1` — `ALE`;
|
||||
- `XWE` — `WE#`;
|
||||
- `XRD` — `RE#`;
|
||||
- `XZCS6AND7` — `CE#`;
|
||||
- `GPIOE0/XINT1` — `R/B#` с внешней подтяжкой 4,7–10 кОм.
|
||||
|
||||
Адреса C28x задаются в 16-битных словах: data `0x100000`, command
|
||||
`0x100001`, address `0x100002`. Используется только младший байт XINTF.
|
||||
|
||||
`WP#` в базовом варианте аппаратный: для режима только чтения соединить с GND
|
||||
через 10 кОм; если позже понадобится PROGRAM/ERASE, заменить на GPIO и реализовать
|
||||
`tms_set_wp()`.
|
||||
|
||||
Задержка в порте приблизительная. После выбора частоты CPU проверить импульсы
|
||||
осциллографом и при необходимости изменить `PNAND_TMS_CPU_MHZ`.
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "parallel_nand_port_tms320f2812.h"
|
||||
#include "DSP281x_Device.h"
|
||||
|
||||
/* C28x addresses are 16-bit word addresses. XA0/XA1 drive CLE/ALE. */
|
||||
#define PNAND_TMS_DATA_REG (*(volatile unsigned int *)0x100000UL)
|
||||
#define PNAND_TMS_CMD_REG (*(volatile unsigned int *)0x100001UL)
|
||||
#define PNAND_TMS_ADDR_REG (*(volatile unsigned int *)0x100002UL)
|
||||
|
||||
#ifndef PNAND_TMS_CPU_MHZ
|
||||
#define PNAND_TMS_CPU_MHZ 150UL
|
||||
#endif
|
||||
|
||||
static void tms_delay_us(unsigned long us)
|
||||
{
|
||||
volatile unsigned long loops = us * (PNAND_TMS_CPU_MHZ / 5UL);
|
||||
while (loops-- != 0UL) asm(" NOP");
|
||||
}
|
||||
|
||||
static int tms_init(void)
|
||||
{
|
||||
EALLOW;
|
||||
GpioMuxRegs.GPEMUX.bit.XINT1_XBIO_GPIOE0 = 0;
|
||||
GpioMuxRegs.GPEDIR.bit.GPIOE0 = 0;
|
||||
|
||||
XintfRegs.XINTCNF2.bit.XTIMCLK = 1;
|
||||
XintfRegs.XINTCNF2.bit.WRBUFF = 0;
|
||||
XintfRegs.XINTCNF2.bit.CLKOFF = 0;
|
||||
XintfRegs.XINTCNF2.bit.CLKMODE = 1;
|
||||
XintfRegs.XTIMING6.bit.XWRLEAD = 3;
|
||||
XintfRegs.XTIMING6.bit.XWRACTIVE = 7;
|
||||
XintfRegs.XTIMING6.bit.XWRTRAIL = 3;
|
||||
XintfRegs.XTIMING6.bit.XRDLEAD = 3;
|
||||
XintfRegs.XTIMING6.bit.XRDACTIVE = 7;
|
||||
XintfRegs.XTIMING6.bit.XRDTRAIL = 3;
|
||||
XintfRegs.XTIMING6.bit.USEREADY = 0;
|
||||
XintfRegs.XTIMING6.bit.READYMODE = 0;
|
||||
XintfRegs.XTIMING6.bit.XSIZE = 3;
|
||||
EDIS;
|
||||
asm(" RPT #7 || NOP");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tms_select(void) { /* XZCS6AND7 формируется каждым доступом. */ }
|
||||
static void tms_deselect(void) { }
|
||||
static void tms_write_command(pnand_octet_t v) { PNAND_TMS_CMD_REG = v & 0xFFU; }
|
||||
static void tms_write_address(pnand_octet_t v) { PNAND_TMS_ADDR_REG = v & 0xFFU; }
|
||||
static void tms_write_data(pnand_octet_t v) { PNAND_TMS_DATA_REG = v & 0xFFU; }
|
||||
static pnand_octet_t tms_read_data(void) { return PNAND_TMS_DATA_REG & 0xFFU; }
|
||||
static int tms_is_ready(void) { return GpioDataRegs.GPEDAT.bit.GPIOE0 != 0; }
|
||||
static void tms_set_wp(int enabled)
|
||||
{
|
||||
/* Шаблон платы фиксирует WP# аппаратно. См. README порта. */
|
||||
(void)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
|
||||
};
|
||||
|
||||
const parallel_nand_port_t *parallel_nand_port_tms320f2812(void)
|
||||
{
|
||||
return &tms_port;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef PARALLEL_NAND_PORT_TMS320F2812_H
|
||||
#define PARALLEL_NAND_PORT_TMS320F2812_H
|
||||
|
||||
#include "parallel_nand_port.h"
|
||||
|
||||
const parallel_nand_port_t *parallel_nand_port_tms320f2812(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user