VGA пиксельная группировка на STM32 - PullRequest
1 голос
/ 23 января 2020

У меня есть код, который отображает один пиксель на экране через VGA, но я немного застрял в том, как я могу установить несколько пикселей на экране, где я хочу их. Я установил два таймера для вертикальной синхронизации c и горизонтальной синхронизации c, а затем, используя прерывание V-Syn c, установил флаг, позволяющий PA8 переключаться и выводить пиксель в правильное время на основе SetCompare значение, которое я установил на канале таймера. STM32f103c8 также разогнан до 128 МГц. Вот код:

#include "Arduino.h"

//640x480 at 60Hz
static volatile int vflag = 0;

void setup() {

#define FLASH_ACR (*(volatile uint32_t*)(0x40022000))
FLASH_ACR = 0b110010; //enable flash prefetch and wait state to increase stability at higher freq

pinMode(PA0, PWM); //31,468.75Hz (Horizontal Sync) (Channel 1)
Timer2.pause();
Timer2.setOverflow(4067); //reload register value 
Timer2.setPrescaleFactor(1); //number that divides main clock 
Timer2.setCompare(1, 488); //12% duty cycle (Syncpulse/Wholeline) 
Timer2.setCompare(2, 2000); //0-4067 = vertical line going left or right respectively
Timer2.attachInterrupt(2, TRIGGER);
Timer2.refresh();
Timer2.resume();

pinMode(PA6, PWM); //60Hz (Vertical Sync) (Channel 1)
Timer3.pause();
Timer3.setOverflow(4183); //reload register value 
Timer3.setPrescaleFactor(510); //number that divides main clock 
Timer3.setCompare(1, 16); //0.38% duty cycle (Syncpulse/Wholeframe) 
Timer3.setCompare(2, 2000); //0-4183 = horizontal line going up or down respectively
Timer3.attachInterrupt(2, TRIGGER2); 
Timer3.refresh();
Timer3.resume();

pinMode(PA8, OUTPUT); //need to set PinMode in order for the ODR register to work

}

void loop() {

}

void TRIGGER(){
if(vflag==1){ 
__asm__ volatile (
"ldr r0, =(0x4001080C) \n\t" //GPIOA base address is 0x40010800 and ODR offset is 0x0C
"ldr r1, =(1<<8) \n\t" //turn on PA8
"ldr r2, =0 \n\t" //turn off PA8 

"str r1, [r0] \n\t" //turn on PA8
"str r2, [r0] \n\t" //turn off PA8

);
vflag = 0; //we set the vflag back to zero when were done outputing pixels.
}
}

Я понимаю, что есть графические дефекты / глюки, и код можно улучшить, но я пытаюсь сосредоточиться на том, как в теории это работает. То, что я хочу сделать - это отобразить слово на экране, это слово будет состоять из букв, а эти буквы будут состоять из групп пикселей. Так какой же самый лучший (или самый простой) способ сгруппировать пиксели и выполнить их несколько раз на экране? Или как это обычно делается?

1 Ответ

1 голос
/ 24 января 2020

Я не кодирую для STM32 , поэтому даже код выглядит для меня чуждо, однако звучит так, будто вы жестко кодируете отдельные пиксели с таймером ... и генерируете сигнал VGA с помощью некоторого GPIO. Эта комбинация методов проблематична c для использования в программируемой графике.

Я использую AVR32 ( UC3A с гораздо более медленными по сравнению с вашими часами) для создания изображения VGA с использованием:

  1. экранный буфер (видеорам)

    просто у меня все изображение экрана хранится в памяти MCU. Таким образом, вы можете просто изменить его содержимое без изменения выходного кода VGA ...

    Проблема в том, что вам необходимо иметь достаточно памяти для изображения (закодировано таким образом, чтобы обеспечить прямую передачу на разъем VGA). Я использую AVR32 с 16 + 32 + 32 КБ ОЗУ, но у большинства микроконтроллеров гораздо меньше ОЗУ (изображения STATI c могут быть сохранены в СППЗУ, но тогда будет невозможно изменить вывод изображения). Таким образом, в случае, если вам не хватает либо более низкого разрешения для размещения в памяти, либо для добавления внешней памяти в вашу систему.

  2. используйте встроенную периферийную периферию HW для генерации сигнала VGA

    У меня работает больше версий генерации сигнала VGA:

    • SDRAM ... с использованием интерфейса SDRAM MCU
    • SM C ... с использованием SM C интерфейс MCU
    • SS C ... с использованием синхронного последовательного интерфейса

    Все сводится к копированию экранного буфера в интерфейс ввода-вывода на VGA Точечные часы (~ 30 МГц) . Затем на стороне HW, объединяя используемые выводы MCU в сигналы VGA

    для скорости, вы можете использовать DMA.

    Помимо всего этого вам нужно генерировать сигналы syn c, и это все .

    Посмотрите на таблицу данных MCU и найдите интерфейс, способный к синхронной передаче как минимум 3-битных данных (R, G, B) с тактовой частотой VGA. Чем ближе часы к точечным часам VGA, тем лучше (поскольку некоторые VGA-мониторы и ЖК-дисплеи не допускают слишком большой разницы)

    синхросигналы c могут быть жестко закодированы или даже закодированы в видеопамяти.

    Самый быстрый способ - использовать последовательный интерфейс, но на выходе получается только черно-белый, а не RGB (если вы не получили 3 SS C единиц / каналов), однако вы можете отправлять целые 8/16/32 пикселей одновременно или напрямую через DMA поэтому у MCU есть время для других вещей, а также требуется гораздо меньше VRAM.

    Мой любимый интерфейс SDRAM (используя только его шину данных)

Здесь изображение из моей системы :

AVR32 VGA output

Взаимосвязь шахты выглядит следующим образом (с использованием версии интерфейса MCRs SDRAM):

VGA <-  AT32UC3A0512
 R      PX10 (EBI_D0)
 G      PX09 (EBI_D1)
 B      PX08 (EBI_D2)
 Bright PX07 (EBI_D3)*
 HS     PA03
 VS     PA04

Здесь соответствующий источник:

VGA_EBI_SDRAM C .h:

//------------------------------------------------------------------------------------------------
#define _PA_VGA_HS  8
#define _PA_VGA_VS 16
#define _PAmo      24
volatile avr32_gpio_port_t *port_PA=&GPIO.port[AVR32_PIN_PA00>>5];
volatile U8 *SDRAM=(U8*)AVR32_EBI_CS0_ADDRESS;
//------------------------------------------------------------------------------------------------
//--- VGA 640x480x4 60Hz -------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
#define VRAM_xs 304
#define VRAM_ys 400
#define VRAM_bs 4
#define VRAM_ls (VRAM_xs>>1)
U8      VRAM[VRAM_ls*VRAM_ys];
U8      VRAM_empty[VRAM_ls];
// Horizontal timing [us]
#define VGA_t0 3
#define VGA_t1 5
#define VGA_t2 32
// Vertikal timing [lines ~31.817us] aby voslo viac bodov tak je to natiahnute na 32us++
#define VGA_ys 525
#define VGA_VS 2
#define VGA_y0 (36+40)
#define VGA_y1 (VGA_y0+VRAM_ys)
//------------------------------------------------------------------------------------------------
void VGA_init();
void VGA_screen();
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
void VGA_init()
    {
    system_init();
    Disable_global_interrupt();
    gpio_configure_pins(port_PA,_PAmo,GPIO_DIR_OUTPUT|GPIO_INIT_HIGH);
    static const gpio_map_t EBI_GPIO_MAP[] =
        {
        { AVR32_EBI_DATA_0_PIN, AVR32_EBI_DATA_0_FUNCTION},
        { AVR32_EBI_DATA_1_PIN, AVR32_EBI_DATA_1_FUNCTION},
        { AVR32_EBI_DATA_2_PIN, AVR32_EBI_DATA_2_FUNCTION},
        { AVR32_EBI_DATA_3_PIN, AVR32_EBI_DATA_3_FUNCTION},
        };
    gpio_enable_module(EBI_GPIO_MAP, sizeof(EBI_GPIO_MAP) / sizeof(EBI_GPIO_MAP[0]));

    AVR32_SDRAMC.mr=0;  // normal mode
    AVR32_SDRAMC.tr=0;  // no refresh (T=0)
    AVR32_SDRAMC.cr=
         (AVR32_SDRAMC_CR_NC_11_COLUMN_BITS <<AVR32_SDRAMC_CR_NC_OFFSET)
        |(AVR32_SDRAMC_CR_NR_13_ROW_BITS    <<AVR32_SDRAMC_CR_NR_OFFSET)
        |(AVR32_SDRAMC_CR_NB_TWO_BANKS      <<AVR32_SDRAMC_CR_NB_OFFSET)
        |(AVR32_SDRAMC_CAS_ONE_CYCLE        <<AVR32_SDRAMC_CR_CAS_OFFSET)
        |(AVR32_SDRAMC_DBW_16_BITS          <<AVR32_SDRAMC_CR_DBW_OFFSET)
        |(0                                 <<AVR32_SDRAMC_TWR_OFFSET)
        |(0                                 <<AVR32_SDRAMC_TRC_OFFSET)
        |(0                                 <<AVR32_SDRAMC_TRP_OFFSET)
        |(0                                 <<AVR32_SDRAMC_TRCD_OFFSET)
        |(0                                 <<AVR32_SDRAMC_TRAS_OFFSET)
        |(0                                 <<AVR32_SDRAMC_TXSR_OFFSET);
    AVR32_SDRAMC.hsr=AVR32_SDRAMC_HSR_DA_MASK;
    AVR32_SDRAMC.mdr=AVR32_SDRAMC_MDR_MD_SDRAM;

    // map SDRAM CS -> memory space
    AVR32_HMATRIX.sfr[AVR32_EBI_HMATRIX_NR]|=1<<AVR32_EBI_SDRAM_CS;
    AVR32_HMATRIX.sfr[AVR32_EBI_HMATRIX_NR];

    U32 a;
    for (a=0;a<VRAM_ls*VRAM_ys;a++) VRAM[a]=0;
    for (a=0;a<VRAM_ls;a++) VRAM_empty[a]=0;
    }
//------------------------------------------------------------------------------------------------
void VGA_screen()
    {
    U32 a,x,y,c,PA,t0;
    wait_start(t0);
    for (;;)
        {
        for (PA=_PAmo,a=0,y=0;y<VGA_ys;y++)
            {
            wait_start(t0);
            if (y==     0) PA^=_PA_VGA_VS; else PA^=0;  // VS on
            if (y==VGA_VS) PA^=_PA_VGA_VS; else PA^=0;  // VS off
            PA^=_PA_VGA_HS;                             // HS on
            port_PA->ovrc=PA^_PAmo;
            port_PA->ovrs=PA;
            wait_us(t0,VGA_t0);
            PA^=_PA_VGA_HS;                             // HS off
            port_PA->ovrc=PA^_PAmo;
            port_PA->ovrs=PA;
            wait_us(t0,VGA_t1);
            *SDRAM=0; // blank (black)
            if ((y>=VGA_y0)&&(y<VGA_y1))
             for (x=0;x<VRAM_ls;x++)
                 {
                 c=VRAM[a];
                 *SDRAM=c>>4; // write pixel into SDRAM interface (address is ignored as I use only data bus pins)
                 a++;
                 *SDRAM=c; // write pixel into SDRAM interface (address is ignored as I use only data bus pins)

                 }
            *SDRAM=0; // blank (black)
            wait_us(t0,VGA_t2);
            }
        }
    }
//------------------------------------------------------------------------------------------------

Main. cpp:

//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
#include "System\include.h"
#include "pic_zilog_inside.h"
//#include "VGA_EBI_SMC.h"
#include "VGA_EBI_SDRAMC.h"
//#include "VGA_SSC.h"
//------------------------------------------------------------------------------------------------
void pic_copy(U8 *dst,U32 dst_xs,U32 dst_ys,U32 dst_bs,U8 *src,U32 src_xs,U32 src_ys,U32 src_bs)
    {
    U32 x0,y0,a0,l0;
    U32 x1,y1,a1,l1;
    U32 a; U8 c,m;
    l0=1; l1=1;
    if (dst_bs==1) l0=dst_xs>>3;
    if (dst_bs==2) l0=dst_xs>>2;
    if (dst_bs==4) l0=dst_xs>>1;
    if (dst_bs==8) l0=dst_xs;
    if (src_bs==1) l1=src_xs>>3;
    if (src_bs==2) l1=src_xs>>2;
    if (src_bs==4) l1=src_xs>>1;
    if (src_bs==8) l1=src_xs;

    for (a0=0;a0<dst_ys*l0;a0++) dst[a0]=0;

    for (y0=0;y0<dst_ys;y0++)
        {
        y1=(y0*(src_ys-1))/(dst_ys-1);
        a0=l0*y0;
        a1=l1*y1;
        for (x0=0;x0<dst_xs;x0++)
            {
            x1=(x0*(src_xs-1))/(dst_xs-1);
            c=0;
            if (src_bs==1)
                {
                c=src[a1+(x1>>3)];
                c>>=7-(x1&7);
                c&=1;
                }
            if (src_bs==4)
                {
                c=src[a1+(x1>>1)];
                if (U32(x0&1)==0) c>>=4;
                c&=15;
                }
            if (dst_bs==1)
                {
                c<<=7-(x0&7);
                a=a0+(x0>>3);
                dst[a]|=c; if (!c) dst[a]^=c;
                }
            if (dst_bs==4)
                {
                if (c) c=15;
                if (U32(x0&1)==0) { c<<=4; m=0x0F; } else m=0xF0;
                a=a0+(x0>>1);
                dst[a]&=m;
                dst[a]|=c;
                }
            }
        }
    }
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
int main(void)
    {
    VGA_init();
    pic_copy
        (
        (U8*)VRAM,
        VRAM_xs,
        VRAM_ys,
        VRAM_bs,
        (U8*)pic_zilog_inside,
        pic_zilog_inside_xs,
        pic_zilog_inside_ys,
        pic_zilog_inside_bs
        );
    VGA_screen();
    }
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------

pic_zilog_inside.h:

const U32 pic_zilog_inside_xs=640;
const U32 pic_zilog_inside_ys=480;
const U32 pic_zilog_inside_bs=1;
const U32 pic_zilog_inside[]= // hard coded image
    {
    0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,
    0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
...
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,
    0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,
    };

Функция pic_copy просто копирует жестко закодированное изображение в VRAM.

Функция VGA_screen() создает бесконечный сигнал VGA l oop, поэтому другие задачи должны быть закодированы в ISR или жестко закодированы в код паузы или между отдельными кадрами (однако это действительно требует моей настройки, так как я получил небольшие тактовые частоты MCU, поэтому нет много места для других дел). VRAM кодируется в 16 цветах (4 бита на пиксель)

         8 4 2 1
Brightness B G R

Яркость должна просто добавить некоторое напряжение к R, G, B с небольшим количеством резисторов и диодов, но более новая реализация реализована на стороне HW, вместо этого у меня есть эта схема (только 8 цветов):

VGA

Диоды должны быть быстрыми при одинаковом барьерном напряжении, а конденсаторы - 1 нФ. Это позволяет избежать сбоев изображения из-за использованной синхронизации интерфейса шины данных. Также диоды необходимы для яркости, если они будут добавлены в будущем, для получения дополнительной информации см. Проблему тока R2R здесь:

...