STM32上的VGA像素分组

问题描述 投票:0回答:1

我有一些代码可以通过VGA在屏幕上显示单个像素,但是在如何在屏幕上设置多个像素的位置上我有些固定。我设置了两个用于垂直同步和水平同步的计时器,然后使用垂直同步中断,我设置了一个标志,以允许PA8根据我在计时器上设置的SetCompare值,在正确的时间切换和输出像素。渠道。 STM32f103c8也超频至128MHz。这是代码:

#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.
}
}

我知道存在图形缺陷/毛病,并且可以改进代码,但我尝试着眼于理论上这是如何工作的。我想要做的是在屏幕上显示一个单词,该单词将由字母组成,而这些字母将由像素组组成。那么,最好的(或最简单的)对像素进行分组并在屏幕上多次执行的方法是什么?或通常如何做?

graphics arduino stm32 pixel vga
1个回答
0
投票

我没有为STM32编写代码,所以即使代码对我来说看起来也不是,但是听起来您正在使用计时器对单个像素进行硬编码...并通过某些GPIO生成VGA信号。方法的这种组合对于可编程图形使用是有问题的。

我正在使用AVR32UC3A,并且时钟频率比您的慢)来使用以下方法来制作VGA图像:

  1. 屏幕缓冲区(videoram)

    简而言之,我将整个屏幕图像存储在MCU存储器中。因此,您只需更改其内容即可,而无需更改VGA输出代码...

    问题是您需要有足够的存储空间来存储图像(以某种方式进行编码,以便可以直接传输到VGA连接器)。我使用的AVR32具有16 + 32 + 32 KB的RAM,但大多数MCU的RAM少得多(可以将静态图像存储在EPROM中,但无法更改图像输出)。因此,如果您没有足够低的分辨率以适合内存或无法向系统添加外部内存。

  2. 使用集成硬件外围设备生成VGA信号

    我有更多版本的VGA信号发生工作:

    • 使用MCU的SDRAM接口的SDRAM ...
    • SMC ...使用MCU的SMC接口
    • SSC ...使用同步串行接口

    全部归结为将屏幕缓冲区复制到VGA点时钟(〜30MHz)的IO接口。然后在硬件方面,将使用的MCU引脚合并为VGA信号

    为了提高速度,您可以使用DMA。

    首先,您需要生成同步信号,仅此而已。

    因此,请查看您的MCU数据表并找到能够以VGA点时钟速度同步传输至少3位数据(R,G,B)的接口。时钟离VGA点时钟越近越好(因为某些VGA监视器和LCD不能容忍太大的差异)

    同步信号可以在视频内存中进行硬编码甚至编码。

    最快的方法是使用串行接口,但输出仅为黑白,而不是RGB,但是您可以一次或直接通过DMA发送整个8/16/32像素,因此MCU有时间处理其他内容,并且所需的VRAM少得多。

    我最喜欢的是SDRAM接口(仅使用其数据总线)

这里是我的系统的图像:

“

矿井互连看起来像这样(使用MCU的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_SDRAMC.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;
            if ((y>=VGA_y0)&&(y<VGA_y1))
             for (x=0;x<VRAM_ls;x++)
                 {
                 c=VRAM[a];
                 *SDRAM=c>>4;
                 a++;
                 *SDRAM=c;
                 }
            *SDRAM=0;
            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,
    };

函数VGA_screen()在无穷循环中创建VGA信号,因此必须将其他任务编码为ISR或将其硬编码为暂停代码或在各个帧之间(但是,由于我的MCU时钟较小,这在矿山设置中确实有要求,因此没有足够的空间来做其他事情)。 VRAM被编码为16种颜色(每个像素4位)

         8 4 2 1
Brightness B G R

亮度应该只用很少的电阻和二极管为R,G,B加上一些电压,但是较新的在硬件方面实现了,所以我有这个电路(仅8种颜色:]

VGA

功能pic_copy只是将硬编码的图像复制到VRAM。

© www.soinside.com 2019 - 2024. All rights reserved.