数码之家

 找回密码
 立即注册
搜索
查看: 5400|回复: 23

[STM] 利用STM32画320*240点阵圆和支线直线

[复制链接]
发表于 2020-1-23 06:57:55 | 显示全部楼层 |阅读模式


void draw_circle(int x, int y, int r, int color)
{
int a, b, num;
a = 0;
b = r;
while(2 * b * b >= r * r)  // 1/8圆即可
{

draw_point(x + a, y - b,color); // 0~1
draw_point(x - a, y - b,color); // 0~7
draw_point(x - a, y + b,color); // 4~5
draw_point(x + a, y + b,color); // 4~3
draw_point(x + b, y + a,color); // 2~3
draw_point(x + b, y - a,color); // 2~1
draw_point(x - b, y - a,color); // 6~7
draw_point(x - b, y + a,color); // 6~5

a++;

num = (a * a + b * b) - r*r;

if(num > 0)

{
b--;
a--;
}
}
}

//-----------画线。参数:起始坐标,终点坐标,颜色--------

void draw_line(int x1,int y1,int x2,int y2,int color)
{
int dx,dy,e;
dx=x2-x1;
dy=y2-y1;
if(dx>=0)

{

if(dy >= 0) // dy>=0
{
if(dx>=dy) // 1/8 octant

{

e=dy-dx/2;

while(x1<=x2)

{

draw_point(x1,y1,color);

if(e>0){y1+=1;e-=dx;}   

x1+=1;

e+=dy;

}
}
else// 2/8 octant

{

e=dx-dy/2;

while(y1<=y2)

{

draw_point(x1,y1,color);

if(e>0){x1+=1;e-=dy;}   

y1+=1;
e+=dx;

}

}

}

else   // dy<0
{
dy=-dy;   // dy=abs(dy)

if(dx>=dy) // 8/8 octant
{

e=dy-dx/2;

while(x1<=x2)
{

draw_point(x1,y1,color);

if(e>0){y1-=1;e-=dx;}   

x1+=1;
e+=dy;
}
}

else// 7/8 octant

{

e=dx-dy/2;

while(y1>=y2)

{

draw_point(x1,y1,color);

if(e>0){x1+=1;e-=dy;}   
y1-=1;
e+=dx;

}

}

}   

}

else //dx<0
{

dx=-dx; //dx=abs(dx)
if(dy >= 0) // dy>=0
{

if(dx>=dy) // 4/8 octant

{
e=dy-dx/2;
while(x1>=x2)

{

draw_point(x1,y1,color);
if(e>0){y1+=1;e-=dx;}   
x1-=1;
e+=dy;

}
}

else// 3/8 octant

{
e=dx-dy/2;
while(y1<=y2)
{
draw_point(x1,y1,color);
if(e>0){x1-=1;e-=dy;}   
y1+=1;
e+=dx;
}
}
}
else   // dy<0
{
dy=-dy;   // dy=abs(dy)

if(dx>=dy) // 5/8 octant

{

e=dy-dx/2;

while(x1>=x2)
{
draw_point(x1,y1,color);
if(e>0){y1-=1;e-=dx;}   
x1-=1;
e+=dy;
}
}
else// 6/8 octant
{
e=dx-dy/2;
while(y1>=y2)
{
draw_point(x1,y1,color);
if(e>0){x1-=1;e-=dy;}   
y1-=1;
e+=dx;
}
}
}   
}
}
// 区域填色函数
// 参数:开始列数,结束列数,开始行数,结束行数,颜色
void fill_fb(int start_x, int end_x, int start_y, int end_y, unsigned short val)
{
int i, j;
for(i = start_y; i < end_y; i++)
{
for(j = start_x; j < end_x; j++)
{
fb[i*480 + j] = val;

}
}
}
void clr_fb(void)
{
int i, j;
for(i = 0; i < 272; i++)
{
for(j = 0; j < 480; j++)
{
fb[i*480 + j] = 0;
}
}
}


下面先简要介绍常用的画圆算法(Bresenham算法),然后再具体阐述笔者对该算法的改进。

一个圆,如果画出了圆上的某一点,那么可以利用对称性计算余下的七段圆弧:Plot(x,y),Plot(y,x),Plot(y,-x),Plot(x,-y),Plot(-x,-y),Plot(-y,-x),Plot(-y,x),Plot(-x,y)。

1、Bresenham 画圆算法。Bresenham算法的主要思想是:以坐标原点(0,0)为圆心的圆可以通过0度到45°的弧计算得到,即x从0增加到半径,然后利用对称性计算余下的七段圆弧。当x从0增加到时,y从R递减到。

设圆的半径为R,则圆的方程为:

f(x,y)=(x+1)2+y2-R2=0   (1)

假设当前列(x=xi列)中最接近圆弧的像素已经取为P(xi,yi),根据第二卦限1/8圆的走向,下一列(x=xi+1列)中最接近圆弧的像素只能在P的正右方点H(xi+1,yi)或右下方点L(xi+1,yi-1)中选择,如图1所示。Bresenham画圆算法采用点T(x,y)到圆心的距离平方与半径平方之差D(T)作为选择标准,即

D(T)=(x+1)2+y2-R2 (2)

通过比较H、L两点各自对实圆弧上点的距离大小,即根据误差大小来选取,具有最小误差的点为绘制点。根据公式(2)得:

对H(xi+1,yi)点有:D(H)=(xi+1)2+yi2-R2;
对L(xi+1,yi-1)点有:D(L)=(xi+1)2+(yi-1)2-R2;
根据Bresenham画圆算法,则选择的标准是:
如果|D(H)|<|D(L)|,那么下一点选取H(xi+1,yi);
如果|D(H)|>|D(L)|,那么下一点选取L(xi+1,yi-1);
如果|D(H)|=|D(L)|,那么下一点可以取L(xi+1,yi-1),也可以选取H(xi+1,yi),我们约定选取H(xi+1,yi)。

图1  Bresenham画圆算法点的选取

综合上述情况,得:

当|D(H)|>|D(L)|时,选取L点(xi+1,yi-1)为绘制点坐标;
当|D(H)|<|D(L)|时,选取H点(xi+1,yi)为绘制点坐标。
然后将选取的点坐标作为当前坐标,重复上述过程直至xi=或者yi=为止,(xi,yi)的初始值为(0,R)。

以上便是Bresenham算法的主要思想,但是上述算法是在一个假设下:以坐标原点(0,0)为圆心。该假设实际上只是为了方便算法的研究。但在实际嵌入式LCD显示设备中,往往圆心坐标不是(0,0)点,而是以左上角为(0,0)点,这样就使得在实际运用中,需要对这个算法做很大的改进。

另外,如果完全按照Bresenham画圆算法,那么就会涉及到浮点运算,这使得嵌入式编程十分烦琐,因为本系统中所有数据都是整型的,因此在这方面也要作一定的改进。下面根据本系统中嵌入式硬件特点和数据结构得特点,对这个算法进行改进。

2、改进的Bresenham画圆算法。先假设起始点为(R,0),令Pi=(xi,yi)为当前的一点,那么我们就需要在Ti=(xi,yi+1)和Si=(xi-1,yi+1)中选取一点,如图2所示。

图2 嵌入式LCD画圆时点的选取

设(xi-1/2+e,yi+1)为S和T之间圆上的点,e是S、T中点到圆上点的误差,带入圆的方程(1)得:

f(xi-1/2+e,yi+1)=(xi-1/2+e)2+(yi+1)2-R2=f(xi-1/2,yi+1)+2(xi-1/2)e+e2=0   (3)

在式(3)中,令

di="f"(xi-1/2,yi+1)=-2(xi-1/2)e-e2(4)

如果e<0,那么di>0,因此选择S=(xi-1,yi+1),根据(3)与(4)得:

di+1=f(xi-1-1/2,yi+1+1)=di-2(xi-1)+2(yi+1)+1=di+2(yi+1-xi+1)+1 (5)

如果e30,那么di£0,因此选择T=(xi,yi+1),根据(3)与(4)得:

di+1=f(xi-1/2,yi+1+1)=di+2yi+1+1  (6)

起始点是(R,0)的时候,根据(4)得di的初始值d0就是:

d0=f(R-1/2,0+1)=(R-1/2)2+1-R2=5/4-R=1-R(由于编程中所用数据类型均为整型,故取1-R)。

综合上述情况,得:

当选取S=(xi-1,yi+1)时,那么di+1=di+2(yi+1-xi+1)+1;

当选取T=(xi,yi+1)时,那么di+1=di+2yi+1+1;

然后将选取的点坐标作为当前坐标,重复上述过程直至x=y,而不是xi=或者yi=,这样就可以不用作浮点数计算了。

本项目中的LCD像素为640×480点阵,并且数据是八位的,当横坐标和纵坐标超过255时,那么数据就不能一次传送成功,因此需要通过字节操作来设定高字节,然后再传送低字节。因此,每次画圆上的点时要传送的参数至少是六个,圆心坐标是四个(因为要考虑圆心坐标可能大于255,因此要对其圆心坐标设置高、低字节),另外两个是圆上的点相对于圆心的坐标,但是最后要画一个点,需要四个参数,即改进的画圆算法为六个参数输入,四个参数输出。

用C语言在嵌入式LCD上实现改进的Bresenham画圆算法的部分代码如下:

voidMidBresenhamcircle(int R)
{
intx,y,d;
x="0";y=R;d=1-R;  
while(x<y)
{circlePoint(x,y);  
if(d<0)  d+=2*x+3;  

   else

{d+=2*(x-y)+5;

  y--;

}

  x++;

delay(900000);

}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 3家元 +50 收起 理由
泗水之阳 + 10 鼠年吉祥/新春快乐
家睦 + 30
人艰不拆了 + 10

查看全部打赏

 楼主| 发表于 2020-1-23 06:58:33 | 显示全部楼层
:lol:6666,哈哈哈哈哈
回复 支持 反对

使用道具 举报

头像被屏蔽
发表于 2020-1-23 08:41:46 | 显示全部楼层

回帖奖励 +1 家元

以前在文曲星上乱画,
速度很不理想,

看来是没考虑到计算量的问题,
只想着精确了
回复 支持 反对

使用道具 举报

发表于 2020-1-23 09:44:01 | 显示全部楼层

回帖奖励 +1 家元

单片机可能更难画呢:lol::lol::lol:
回复 支持 反对

使用道具 举报

发表于 2020-1-23 10:07:12 | 显示全部楼层
这是高手。这是高手。这是高手。
回复 支持 反对

使用道具 举报

发表于 2020-1-23 10:43:50 | 显示全部楼层
看平台,Cortex-M4以上内核就有FPU了,浮点数就没那么麻烦了。
回复 支持 反对

使用道具 举报

发表于 2020-1-23 10:56:50 | 显示全部楼层
这两个在各种液晶的历程里面不是都有吗?
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2020-1-23 13:44:02 | 显示全部楼层
netbeetle 发表于 2020-1-23 10:56
这两个在各种液晶的历程里面不是都有吗?

不是吧,《字数补丁》
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-23 13:47:38 | 显示全部楼层
lyy-cy 发表于 2020-1-23 10:07
这是高手。这是高手。这是高手。

还行《字数补丁66》:lol:
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-23 13:48:22 | 显示全部楼层
yich 发表于 2020-1-23 08:41
以前在文曲星上乱画,
速度很不理想,

可能吧《字数补丁66》:smile:
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-23 13:49:00 | 显示全部楼层
la45088d1 发表于 2020-1-23 10:43
看平台,Cortex-M4以上内核就有FPU了,浮点数就没那么麻烦了。

嗯嗯《字数补丁66》
回复 支持 反对

使用道具 举报

发表于 2020-1-23 23:07:29 | 显示全部楼层
回复 支持 反对

使用道具 举报

发表于 2020-1-25 13:39:28 | 显示全部楼层
qq1329491328 发表于 2020-1-23 13:44
不是吧,《字数补丁》

//******************************************************************
//º¯ÊýÃû£º  _draw_circle_8
//×÷Õߣº    xiao·ë@È«¶¯µç×Ó
//ÈÕÆÚ£º    2013-02-22
//¹¦ÄÜ£º    8¶Ô³ÆÐÔ»­Ô²Ëã·¨(ÄÚ²¿µ÷ÓÃ)
//ÊäÈë²ÎÊý£º(xc,yc) :Ô²ÖÐÐÄ×ø±ê
//                         (x,y):¹â±êÏà¶ÔÓÚÔ²ÐĵÄ×ø±ê
//                 c:Ìî³äµÄÑÕÉ«
//·µ»ØÖµ£º  ÎÞ
//Ð޸ļǼ£ºÎÞ
//******************************************************************  
void _draw_circle_8(int xc, int yc, int x, int y, u16 c)
{
        GUI_DrawPoint(xc + x, yc + y, c);

        GUI_DrawPoint(xc - x, yc + y, c);

        GUI_DrawPoint(xc + x, yc - y, c);

        GUI_DrawPoint(xc - x, yc - y, c);

        GUI_DrawPoint(xc + y, yc + x, c);

        GUI_DrawPoint(xc - y, yc + x, c);

        GUI_DrawPoint(xc + y, yc - x, c);

        GUI_DrawPoint(xc - y, yc - x, c);
}

//******************************************************************
//º¯ÊýÃû£º  gui_circle
//×÷Õߣº    xiao·ë@È«¶¯µç×Ó
//ÈÕÆÚ£º    2013-02-22
//¹¦ÄÜ£º    ÔÚÖ¸¶¨Î»Öû­Ò»¸öÖ¸¶¨´óСµÄÔ²(Ìî³ä)
//ÊäÈë²ÎÊý£º(xc,yc) :Ô²ÖÐÐÄ×ø±ê
//                 c:Ìî³äµÄÑÕÉ«
//                         r:Ô²°ë¾¶
//                         fill:Ìî³äÅжϱêÖ¾£¬1-Ìî³ä£¬0-²»Ìî³ä
//·µ»ØÖµ£º  ÎÞ
//Ð޸ļǼ£ºÎÞ
//******************************************************************  
void gui_circle(int xc, int yc,u16 c,int r, int fill)
{
        int x = 0, y = r, yi, d;

        d = 3 - 2 * r;


        if (fill)
        {
                // Èç¹ûÌî³ä£¨»­ÊµÐÄÔ²£©
                while (x <= y) {
                        for (yi = x; yi <= y; yi++)
                                _draw_circle_8(xc, yc, x, yi, c);

                        if (d < 0) {
                                d = d + 4 * x + 6;
                        } else {
                                d = d + 4 * (x - y) + 10;
                                y--;
                        }
                        x++;
                }
        } else
        {
                // Èç¹û²»Ìî³ä£¨»­¿ÕÐÄÔ²£©
                while (x <= y) {
                        _draw_circle_8(xc, yc, x, y, c);
                        if (d < 0) {
                                d = d + 4 * x + 6;
                        } else {
                                d = d + 4 * (x - y) + 10;
                                y--;
                        }
                        x++;
                }
        }
}
随便找了,方法基本相同。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-25 14:16:30 | 显示全部楼层
netbeetle 发表于 2020-1-25 13:39
//******************************************************************
//º¯ÊýÃû£º  _draw_cir ...

:lol:thanks《字数补丁》
回复 支持 反对

使用道具 举报

发表于 2020-1-26 10:00:52 | 显示全部楼层
netbeetle 发表于 2020-1-25 13:39
//******************************************************************
//º¯ÊýÃû£º  _draw_cir ...

不错,收藏了,谢谢!
回复 支持 反对

使用道具 举报

发表于 2020-1-28 22:52:17 | 显示全部楼层
直接用EMWIN岂不是更加方便,EMWIN在STM32是免费使用的
回复 支持 反对

使用道具 举报

发表于 2020-1-28 23:44:15 | 显示全部楼层
推荐楼主《计算机图形学》,我那时候凭感觉写了画贝塞尔曲线,直到知道这玩意才知道我那时候多么愚蠢,里面都有教:cry:
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-29 12:01:31 | 显示全部楼层
cbcb4cb 发表于 2020-1-28 23:44
推荐楼主《计算机图形学》,我那时候凭感觉写了画贝塞尔曲线,直到知道这玩意才知道我那时候多么愚蠢,里面 ...

哈哈哈,好吧啊
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-29 12:02:09 | 显示全部楼层
hujj 发表于 2020-1-26 10:00
不错,收藏了,谢谢!

嗯嗯,谢谢回复
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-29 12:03:11 | 显示全部楼层
go2net 发表于 2020-1-28 22:52
直接用EMWIN岂不是更加方便,EMWIN在STM32是免费使用的

用系统浪费stm32资源
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-4-26 13:12 , Processed in 0.124800 second(s), 12 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

快速回复 返回顶部 返回列表