cancel
Showing results for 
Search instead for 
Did you mean: 

LTDC Layer1 is only a black box on the STM32N6570-DK

Webah
Visitor

Hello everybody,

 

I am currently trying to get the LTDC running on the STM32N6570-DK without using the HAL drivers.
The LTDC itself seems to work partially, because the background color configured via LTDC->BCCR is displayed correctly and can even be changed.

However, as soon as I enable Layer1 with a framebuffer, I only get a solid black rectangle in the top left corner instead of the framebuffer contents.

Current configuration:

  • STM32N6570-DK
  • RGB565
  • 200x200 test framebuffer
  • Framebuffer address: 0x24064000
  • Currently running on FSBL

 

What i observe:

  • The framebuffer memory is filled correctly. (checked in the memory browser)
  • If Layer1 is enabled there is a black rectangle (200x200) in the top left corner.
  • If Layer1 is disabled, clearly there is just the background.
  • LTDC register values appear correct
    • PFCR = 2
    • CR = 1
    • CFBLNR = 200
    • CFBAR = 0x24064000

At this point i suspect that the LTDC master cannot properly access/read the selected AXI SRAM region, even though the CPU can. I also tried other memory regions

 

My Questions:

  1. Are additional STM32N6-specific LTDC AXI master / RAM access enables required?
  2. Is there a recommended framebuffer memory region for LTDC on STM32N6570?
  3. Does GTZC / security attribution affect LTDC framebuffer access by default?
  4. Has anyone successfully used LTDC without HAL on STM32N6 yet?

 

Any hint would be greatly appreciated.

Thanks!

 

Framebuffer Code sample:

#ifndef SIMPLE_LCD_FRAMEBUFFER_H
#define SIMPLE_LCD_FRAMEBUFFER_H

#include <stdint.h>

#define LCD_WIDTH  200
#define LCD_HEIGHT 200

#define LCD_COLOR_BLACK  0x0000
#define LCD_COLOR_WHITE  0xFFFF
#define LCD_COLOR_RED    0xF800
#define LCD_COLOR_GREEN  0x07E0
#define LCD_COLOR_BLUE   0x001F

#define lcd_framebuffer ((volatile uint16_t*)0x24064000UL)

void LCD_Fill(uint16_t color);
//void LCD_DrawPixel(uint32_t x, uint32_t y, uint16_t color);

#endif /* SIMPLE_LCD_FRAMEBUFFER_H */

#include "simple_lcd_framebuffer.h"

void LCD_Fill(uint16_t color){
	volatile uint16_t* ptr = lcd_framebuffer;

    for (uint32_t i = 0; i < LCD_WIDTH * LCD_HEIGHT; i++) {
        ptr[i] = color;
    }
}

 

RCC LTDC Code sample:

void RCC_enable_LTDC_memory(void){
    RCC->MEMENR |= RCC_MEMENR_AXISRAM1EN
                |  RCC_MEMENR_AXISRAM2EN
                |  RCC_MEMENR_AXISRAM3EN
                |  RCC_MEMENR_AXISRAM4EN
                |  RCC_MEMENR_AXISRAM5EN
                |  RCC_MEMENR_AXISRAM6EN;

    (void)RCC->MEMENR;
}

void RCC_enable_LTDC(void){
    RCC->APB5ENSR |= RCC_APB5ENSR_LTDCENS;
    (void)RCC->APB5ENSR;
}

void RCC_reset_LTDC(void){
	RCC->APB5RSTSR |= RCC_APB5RSTSR_LTDCRSTS;
	(void)RCC->APB5RSTSR;

	delay_ms(10);

    RCC->APB5RSTCR |= RCC_APB5RSTCR_LTDCRSTC;
    (void)RCC->APB5RSTCR;
}

void RCC_setLTDC_clock_source(uint32_t source){
    source &= 0x3U;   // 2 bits width

    RCC->CCIPR4 &= ~RCC_CCIPR4_LTDCSEL;
    RCC->CCIPR4 |=  (source << RCC_CCIPR4_LTDCSEL_Pos);

    (void)RCC->CCIPR4;
}

void RCC_config_LTDC_clock(void){
    /* PLL4 configuration for 25 MHz pixel clock
     * PLL4 source = HSI (64 MHz), M=8, N=225, P1=6, P2=6
     * VCO = (64/8) * 225 = 1800 MHz
     * PLL4_out = 1800 / (6*6) = 50 MHz
     * IC16 divider = 2  ->  LTDC clock = 25 MHz
     */
    if (!(RCC->SR & RCC_SR_PLL4RDY)) {
        RCC->PLL4CFGR1 &= ~RCC_PLL4CFGR1_PLL4SEL;
        RCC->PLL4CFGR1 = (RCC->PLL4CFGR1 & ~(RCC_PLL4CFGR1_PLL4DIVM | RCC_PLL4CFGR1_PLL4DIVN))
                        | (8UL << RCC_PLL4CFGR1_PLL4DIVM_Pos)
                        | (225UL << RCC_PLL4CFGR1_PLL4DIVN_Pos);

        RCC->PLL4CFGR2 &= ~RCC_PLL4CFGR2_PLL4DIVNFRAC;

        RCC->PLL4CFGR3 = (RCC->PLL4CFGR3 & ~(RCC_PLL4CFGR3_PLL4PDIV1 | RCC_PLL4CFGR3_PLL4PDIV2))
                        | (6UL << RCC_PLL4CFGR3_PLL4PDIV1_Pos)
                        | (6UL << RCC_PLL4CFGR3_PLL4PDIV2_Pos);
        RCC->PLL4CFGR3 |= RCC_PLL4CFGR3_PLL4PDIVEN;
        (void)RCC->PLL4CFGR3;

        RCC->CR |= RCC_CR_PLL4ON;
        while (!(RCC->SR & RCC_SR_PLL4RDY));
    }

    uint32_t ic16sel = RCC_IC16CFGR_IC16SEL_0 | RCC_IC16CFGR_IC16SEL_1;
    uint32_t ic16int = (2UL - 1UL) << RCC_IC16CFGR_IC16INT_Pos;

    RCC->IC16CFGR = (RCC->IC16CFGR & ~(RCC_IC16CFGR_IC16SEL | RCC_IC16CFGR_IC16INT))
                   | ic16sel | ic16int;
    (void)RCC->IC16CFGR;

    RCC->DIVENR |= RCC_DIVENR_IC16EN;
    (void)RCC->DIVENR;

    RCC->CCIPR4 = (RCC->CCIPR4 & ~RCC_CCIPR4_LTDCSEL) | RCC_CCIPR4_LTDCSEL_1;
    (void)RCC->CCIPR4;
}

 

LTDC Code sample:

static void LCD_ConfigGPIO(void){
    uint32_t pa_pins[] = {0, 1, 2, 7, 8, 15};
    for (int i = 0; i < 6; i++)
        GPIO_Config(GPIOA, pa_pins[i], GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);

    uint32_t pb_pins[] = {2, 4, 11, 12, 13, 14, 15};
    for (int i = 0; i < 7; i++)
        GPIO_Config(GPIOB, pb_pins[i], GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);

    GPIO_Config(GPIOD, 8, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);
    GPIO_Config(GPIOD, 9, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);
    GPIO_Config(GPIOD, 15, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);

    GPIO_Config(GPIOE, 11, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);

    uint32_t pg_pins[] = {0, 1, 6, 8, 11, 12};
    for (int i = 0; i < 6; i++)
        GPIO_Config(GPIOG, pg_pins[i], GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);

    GPIO_Config(GPIOH, 3, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);
    GPIO_Config(GPIOH, 4, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);
    GPIO_Config(GPIOH, 6, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_PUPD_NONE, LTDC_AF);

    GPIO_Config(GPIOE, 1, GPIO_MODE_OUTPUT, GPIO_OTYPE_PP, GPIO_PUPD_NONE, GPIO_AF_NONE);
    GPIO_Config(GPIOG, 13, GPIO_MODE_OUTPUT, GPIO_OTYPE_PP, GPIO_PUPD_NONE, GPIO_AF_NONE);
    GPIO_Config(GPIOQ, 3, GPIO_MODE_OUTPUT, GPIO_OTYPE_PP, GPIO_PUPD_NONE, GPIO_AF_NONE);
    GPIO_Config(GPIOQ, 6, GPIO_MODE_OUTPUT, GPIO_OTYPE_PP, GPIO_PUPD_NONE, GPIO_AF_NONE);
}

static void LCD_PowerOn(void){
    GPIO_BSRR_reset(GPIOE, 1);
    delay_ms(10);
    GPIO_BSRR_set(GPIOE, 1);
    delay_ms(10);

    GPIO_BSRR_set(GPIOQ, 3);
    GPIO_BSRR_set(GPIOQ, 6);
    GPIO_BSRR_set(GPIOG, 13);
}

static void LCD_ConfigTiming(void){
    uint32_t hsync = 4U, hbp = 4U, hfp = 4U, width = 800U;
    uint32_t vsync = 4U, vbp = 4U, vfp = 4U, height = 480U;

    LTDC->SSCR = ((hsync - 1U) << LTDC_SSCR_HSW_Pos) |
                 ((vsync - 1U) << LTDC_SSCR_VSH_Pos);

    LTDC->BPCR = ((hsync + hbp - 1U) << LTDC_BPCR_AHBP_Pos) |
                 ((vsync + vbp - 1U) << LTDC_BPCR_AVBP_Pos);

    LTDC->AWCR = ((hsync + hbp + width - 1U) << LTDC_AWCR_AAW_Pos) |
                 ((vsync + vbp + height - 1U) << LTDC_AWCR_AAH_Pos);

    LTDC->TWCR = ((hsync + hbp + width + hfp - 1U) << LTDC_TWCR_TOTALW_Pos) |
                 ((vsync + vbp + height + vfp - 1U) << LTDC_TWCR_TOTALH_Pos);
}

void LCD_Init(void){
    RCC_enable_LTDC_memory();
    RCC_config_LTDC_clock();

    LCD_ConfigGPIO();
    LCD_PowerOn();

    RCC_enable_LTDC();
    RCC_reset_LTDC();

    LTDC->GCR &= ~LTDC_GCR_LTDCEN;

    LCD_ConfigTiming();

    LTDC->GCR &= ~(LTDC_GCR_HSPOL | LTDC_GCR_VSPOL | LTDC_GCR_DEPOL | LTDC_GCR_PCPOL);
    LTDC->GCR |= (LTDC_GCR_DEPOL | LTDC_GCR_PCPOL);

    LTDC->BCCR = 0xFF00FF00UL;

    LTDC->GCR |= LTDC_GCR_LTDCEN;
}

void LCD_SetBackgroundColor(uint8_t r, uint8_t g, uint8_t b){
    while (!(LTDC->CDSR & LTDC_CDSR_VDES));
    while (LTDC->CDSR & LTDC_CDSR_VDES);
    LTDC->BCCR = ((uint32_t)r << 16U) | ((uint32_t)g << 8U) | (uint32_t)b;
}



void LCD_ConfigLayer1(void){
    uint32_t hsync = 4U;
    uint32_t hbp   = 4U;
    uint32_t vsync = 4U;
    uint32_t vbp   = 4U;

    uint32_t pitch = LCD_WIDTH * 2U; // RGB565 = 2 Byte per pixel

    LTDC_Layer1->CR = 0U;

    LTDC_Layer1->RCR = LTDC_LxRCR_GRMSK;

    LTDC_Layer1->WHPCR =
        ((hsync + hbp) << LTDC_LxWHPCR_WHSTPOS_Pos) |
        ((hsync + hbp + LCD_WIDTH - 1U) << LTDC_LxWHPCR_WHSPPOS_Pos);

    LTDC_Layer1->WVPCR =
        ((vsync + vbp) << LTDC_LxWVPCR_WVSTPOS_Pos) |
        ((vsync + vbp + LCD_HEIGHT - 1U) << LTDC_LxWVPCR_WVSPPOS_Pos);

    LTDC_Layer1->PFCR = 2U;        // RGB565
    LTDC_Layer1->CACR = 0xFFU;     // Alpha 255
 
    LTDC_Layer1->BFCR =
        (4U << LTDC_LxBFCR_BF1_Pos) |
        (5U << LTDC_LxBFCR_BF2_Pos);

    LTDC_Layer1->CFBAR = (uint32_t)lcd_framebuffer;

    LTDC_Layer1->CFBLR =
        (pitch << LTDC_LxCFBLR_CFBP_Pos) |
        ((pitch + 3U) << LTDC_LxCFBLR_CFBLL_Pos);

    LTDC_Layer1->CFBLNR = LCD_HEIGHT;

    LTDC_Layer1->PCR &= ~LTDC_LxPCR_YCEN;

    LTDC_Layer1->CR = LTDC_LxCR_LEN;

    LTDC_Layer1->RCR = LTDC_LxRCR_IMR | LTDC_LxRCR_GRMSK;

    while (LTDC_Layer1->RCR & LTDC_LxRCR_IMR) {
    }
}

 

Main Code sample:

int main(void){
    ...
   
    GPIO_Config(GPIOG, LED2_PIN, GPIO_MODE_OUTPUT, GPIO_OTYPE_PP, GPIO_PUPD_NONE, GPIO_AF_NONE);

    LCD_Init();
    LCD_Fill(0xF800);

    uintptr_t addr  = (uintptr_t)lcd_framebuffer;
    uintptr_t start = addr & ~31U;
    uintptr_t size  = (LCD_WIDTH * LCD_HEIGHT * 2U + 31U) & ~31U;

    SCB_CleanDCache_by_Addr((uint32_t*)start, size);

    delay_ms(10);

    LCD_ConfigLayer1();

    ...
}

 

 

0 REPLIES 0