2023-03-20 11:03 AM
Hello,
I am using a STM32H7A3 and a SDRAM MT48LC8M16A2P.
In a project using the emWin graphic library, I would want to store the relative buffer 'aMemory' in SDRAM.
By reading on community and seeing the examples I understood that I have to write a SystemInit_ExtMemCtl function and call it in SystemInit before to call the main function.
In this function I have to configure the GPIOs relative to FMC, configure FMC and send commands to SDRAM.
But after the send of the clock enable command, the CPU halts on the check of the busy bit of the FMC (instruction FMC_Bank5_6_R->SDSR & 0x00000020).
Please could you help me to understand ?
Thanks in advice
Tommaso
PS This is my code:
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmp = 0;
register uint32_t tmpreg = 0, timeout = 0xFFFF;
register __IO uint32_t index;
/* Enable GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface
clock */
RCC->AHB4ENR |= 0x000001F8;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOEEN);
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0xC0CC50CC; //0x000000CC;
GPIOD->AFR[1] = 0xCCCCCCCC; //0xCC000CCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xAAAABABA; //0xAFEAFFFA;
/* Configure PDx pins speed to VERY High speed */
GPIOD->OSPEEDR = 0xFFFFCF0F; //0xF03F000F;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* Configure PDx pins in Pull-up */
GPIOD->PUPDR = 0x00000000; //0x50150005;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xCCCCC0CC; //0xC00000CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAAAABA;
/* Configure PEx pins speed to 100 MHz */
GPIOE->OSPEEDR = 0xFFFFFFCF;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* Configure PEx pins in Pull-up */
GPIOE->PUPDR = 0x00000000; //0x55554005;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0x00CCCCCC; //0x0; //0x00CCCCCC;
GPIOF->AFR[1] = 0xCCCCC000; //0x0; //0xCCCCC000;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA8F7AAA; //0xFFCF7FFF; //0xAABFFAAA;
/* Configure PFx pins speed to 100 MHz */
GPIOF->OSPEEDR = 0xFFC00FFF; //0x00; //0xFFC00FFF;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* Configure PFx pins in Pull-up */
GPIOF->PUPDR = 0x00; //0x55400555;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0x00CCCCCC; //0x00CCCCCC;
GPIOG->AFR[1] = 0xC700007C; //0xC000000C;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0xACFA3AAA; //0xBFFEFAAA;
/* Configure PGx pins speed to 100 MHz */
GPIOG->OSPEEDR = 0xC0030FFF;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* Configure PGx pins in Pull-up */
GPIOG->PUPDR = 0x00; //0x40010555;
/* Connect PHx pins to FMC Alternate function */
GPIOH->AFR[0] = 0x02C00C00; //0xCCC00000;
GPIOH->AFR[1] = 0x00002200; //0xCCCCCCCC;
/* Configure PHx pins in Alternate function mode */
GPIOH->MODER = 0xDCAFE8EF; //0xAAAAABFF;
/* Configure PHx pins speed to 100 MHz */
GPIOH->OSPEEDR = 0x00300C30; //0xFFFFFC00;
/* Configure PHx pins Output type to push-pull */
GPIOH->OTYPER = 0x00000000;
/* Configure PHx pins in Pull-up */
GPIOH->PUPDR = 0x00; //0x55555400;
/* Connect PIx pins to FMC Alternate function */
GPIOI->AFR[0] = 0x00305500; //0xCCCCCCCC;
GPIOI->AFR[1] = 0x00; //0x00000CC0;
/* Configure PIx pins in Alternate function mode */
GPIOI->MODER = 0x7C7C3BAF; //0xFFEBAAAA;
/* Configure PIx pins speed to 100 MHz */
GPIOI->OSPEEDR = 0x00; //0x003CFFFF;
/* Configure PIx pins Output type to push-pull */
GPIOI->OTYPER = 0x00000000;
/* Configure PIx pins in Pull-up */
GPIOI->PUPDR = 0x00; //0x00145555;
/*-- FMC Configuration ------------------------------------------------------*/
/* Enable the FMC interface clock */
(RCC->AHB3ENR |= (RCC_AHB3ENR_FMCEN));
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/*SDRAM Timing and access interface configuration*/
/*LoadToActiveDelay = 2
ExitSelfRefreshDelay = 6
SelfRefreshTime = 4
RowCycleDelay = 6
WriteRecoveryTime = 2
RPDelay = 2
RCDDelay = 2
SDBank = FMC_SDRAM_BANK2
ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9
RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12
MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32
InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4
CASLatency = FMC_SDRAM_CAS_LATENCY_2
WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE
SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2
ReadBurst = FMC_SDRAM_RBURST_ENABLE
ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0*/
FMC_Bank5_6_R->SDCR[0] = 0x00005DD5; //0x00001800;
FMC_Bank5_6_R->SDTR[0] = 0x01105361; //0x00105000;
/* SDRAM initialization sequence */
/* Clock enable command */
FMC_Bank5_6_R->SDCMR = 0x11; //0x00000009;
tmpreg = FMC_Bank5_6_R->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6_R->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
/* PALL command */
FMC_Bank5_6_R->SDCMR = 0x12; //0x0000000A;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6_R->SDSR & 0x00000020;
}
// other commands
}
2023-03-20 12:26 PM
Does the SDRAM initialize and function properly if brought up by HAL code in your BSP / test application?
2023-03-20 01:48 PM
Hi,
thank you for answer me. Yes, I tested the SDRAM with HAL and it works properly. The problem is initialize it at the start in systemInit.
2023-03-20 04:38 PM
Is this using Bank1 or Bank2 ? If Bank2, per the comment, then it's definitely wrong.
Please define the memory configuration in your design, pins, etc.
Ideally attach the equivalent BSP / MSP code fragment that works.
2023-03-20 05:23 PM
Why do you think SDRAM must be initialized in SystemInit? Does the test app initalize it tis way?
Even if emWin has some statically initialized data in SDRAM, you can copy it "manually" in your main(), after normal initialization of FMC, MPU, SDRAM and before any emWin call.
2023-03-20 05:42 PM
That's where it's supposed to be initialized for the CMSIS model.
SystemInit() sets up memories, is called by the startup code, so the C runtime initialization can do it's job. And for C++ so that the constructors can do their thing properly.
Tools like Keil build initialization tables (scatter list), so the scatter loader can do the job without dozens of symbols.
The "do it in main()" method is rather sloppy, but does allow for all statics to be initialized, rather having things in a "pre-execution" state where more care needs to be taken. Much better for the generally oblivious..
2023-03-20 06:02 PM
This is IMHO one of good examples of how coding for small embedded systems differs from "normal". And why main() and main.c better should stay away from C++.
SystemInit for STM32F4 initializes external memory on FMC, but for H7 that has been removed. Too complicated.
Earlier in this forum you've advised to initialize this stuff in a bootloader, so the app won't need to deal with chicken/egg issues.
2023-03-20 11:50 PM
I'm definitely of the opinion that major initialization of the system should be done once and done early.
It helps boot/startup times, and tries to eliminate the chicken/egg cart/horse type situations by contractually doling out what tasks get done where and by whom.
A lot of this is complex and political so STs implementation just dodges it with examples that work just well enough that they can go hey look this works before deftly moving on.
The external memory initialization could be done better with a table implementation for the pins, as this blind writing of constants to the GPIO registers is just minefield of potential issues, especially when dealing with different boards, and potentially custom boards where a handful of pins might need to be reassigned to achieve PCB routing or peripheral combinations.
2023-03-21 12:01 AM
For C++ initialization there is the expectation that the memory and heap subsystem are viable when the constructors are called. The compiler doesn't want deal with system level issues, and halfway through main() to get clocks and memory is too late in the game.
You kind of want clocks and caches set up optimally so initialization can be done more promptly. This is less obvious with the canned examples, but with full real world uses cases the initialization of the statics and constructors can expand to be quite complex and time consuming, and slowness can be perceived by users who expect things to start immediately. Waiting minutes for systems to boot becomes less acceptable as people are exposed to systems and coding methods that are prompt. I'd hate to field technical support where every caller is complaining about the slowness of the product or equipment.