1

我想在 640x480 VGA 显示器上输出一个半径为 100 像素的移动红色圆圈。我被困在如何制作和填写实际的圆圈上。现在我已经看了一下大脑麻木的 Bresenham 算法,但我无法让它们适合我的 verilog 代码。

我已经理论化了我能做什么,但我不完全确定它是可能的。我知道圆的方程是 (xa) (xa)+(yb) (yb)=r*r 其中 (a,b) 是原点。所以我想用RGB颜色填充这个圆圈并将它从上到下移动,即从b = 0到480 at a = 640/2

由于VGA从左到右输出像素,向下迭代,我猜我必须使用嵌套循环。我知道垂直运动的第一个循环从 0 到 480。这是我遇到问题的第二个循环。它从 0 变为 640,但我希望像素在到达 (x,y) (沿圆圈的一个点)时改变颜色,保留该颜色,然后在它们通过 (x+k) 时变回来(其中 k 是水平和弦)。

基本上,如果下面是我圈子的水平和弦:

_黑色.(x,y)___红色(k 像素) _ .(x+k,y) _黑色

reg [8:0] bally;//y coordinate of the center of circle
reg [8:0] rad;//radius 

always @ (posedge clk)
begin
for(VCount=0;VCount<=480;VCount++)
  for(HCount=0;HCount<=640;HCount++)
     if((HCount*HCount>=rad*rad-((VCount-bally)*(VCount-bally)) && HCount*HCount<=((10000-((VCount-bally)*(VCount-bally)))+k)))
        R<=1;
        G<=0;
        B<=0;
     end

我的问题是,我如何用我已经拥有的变量来表示我的 if 条件中的 k?我意识到这可能不是一个 Verilog 问题,但可能是我在基本三角学上失败了,但我的思想真的被困在这上面了。而且,如果我在尝试制作这个圈子时非常偏离(代码方面、逻辑方面、综合方面),请告诉我。任何帮助将不胜感激。

4

2 回答 2

3

我不确定嵌套的 for 循环是否会像您认为的那样运行,至少对于综合设计而言。

我的建议是不要根据它的参数方程生成圆,这是我认为你想要做的。相反,如果圆始终是相同的(即它的半径始终为 100 像素或更小),请预先生成圆的位图并将其作为大精灵存储在 FPGA(块 RAM)内存中。它不会占用你太多空间。精灵中的 0 位将转换为“透明”,而 1 位表示使用与精灵相关的颜色(圆形)绘制此点。

这样,您绘制屏幕的代码(实际上是屏幕的活动部分)将是这样的:

`define SPRLEN 256  // how many pixels in width and heigh the sprite will have.
`define SPRITECOLOR 3'b100;  // RED ball. (RGB, 1-bit per channel)
`define BGCOLOR 3'010;       // GREEN background

reg ballsprite[0:`SPRLEN*`SPRLEN-1]; // A matrix to hold your sprite.
reg [9:0] hcont, vcont; // horizontal and vertical counters for scanning a VGA picture
reg [8:0] ballx, bally; //coordinates of the left-top corner of the ball sprite
reg [7:0] coorx,coory;  //X,Y coordinates of the current sprite position to draw

initial begin  // read your sprite definition into memory. FPGA synthetizable.
  $readmemb ("mysprite.bin", ballsprite);
end

// process to obtain relative coordinates of the current screen position within the sprite 
always @* begin
  coorx = hcont+1-ballx;
  coory = vcont+1-bally;
end

// process to get the next dot from the sprite. We begin at ballx-1,bally-1 because
// there's one clock delay in getting the current sprite dot value.
always @(posedge clk) begin
  if (hcont>=ballx-1 && hcont<=ballx-1+`SPRLEN && vcont>=bally-1 && vcont<=bally-1+`SPRLEN)
    dot_from_ball <= ballsprite[{coory,coorx}];  // i.e. coory*256+coorx
  else
    dot_from_ball <= 1'b0;  // if scan trace is not under the boundaries of the sprite,
                            // then it's transparent color
end

// multiplexer to actually put an RGB value on screen
always @* begin
  if (hcont>=0 && hcont<640 && vcont>=0 && vcont<480) begin
    if (dot_from_ball)
      {R,G,B} = `SPRITECOLOR;
    else
      {R,G,B} = `BGCOLOR;
  end
  else
    {R,G,B} = 3'b000;  //if not into active area, mute RGB
end

您可以为 hcont 和 vcont 生成适当的值,并根据这些计数器何时达到某些值来推断同步信号。

前段时间,我做过类似的事情:在 VGA 屏幕上绘制一个移动的精灵。我是用 Handel C 做的,但逻辑是一样的。你可以在这里看到它: http ://www.youtube.com/watch?v=wgDSzC-vGZ0

于 2013-12-02T19:03:01.943 回答
2

你不想要这样的循环。如果您想动态生成一个圆(而不是使用位图),请在单独的进程(always块)中跟踪您的当前坐标。每个 always 块将在 VGA 时钟的每个滴答上运行,因此每次块执行时都有一个新的像素位置。

然后有另一个always块查看这些 x 和 y 坐标并确定当前点是否在圆中。根据该决定,您可以选择前景色或背景色。同样,这按顺序对每个像素运行一次。

棘手的部分是决定当前点是否在圆圈中。

如果这看起来有点难以开始,请画一个正方形,因为它的方程式非常简单。一旦你明白了这一点并准确地理解了它的工作原理和原因,你就可以毕业到圆形(和其他参数定义的形状)。要找到动态渲染这些形状的方法,当您扫描图像时,您可能会发现术语“扫描线渲染”或“增量渲染”很有帮助。


再想一想,难道不是圆的标准方程:

x^2 + y^2 = r^2

可以呈现为不等式以返回圆内的点:

x^2 + y^2 < r^2

扩展以允许中心点的变化:

(x-xc)^2 + (y-yc)^2 < r^2

这意味着对于每个像素,您从 x 和 y 中减去中心点,将它们平方并与r^2. 如果它小于 r^2,则绘制像素。

在老式的 FPGA 中,这将是很多逻辑,但是如今 3 个减法器和 2 个乘法器什么都不是(或者,如果您以像素时钟的倍数运行,则可以共享乘法器,但要以多路复用器为代价输入)

于 2013-12-03T10:39:43.733 回答