sujingliang 发表于 2023-11-24 22:05:20

STC89C52驱动 16x64LED点阵模块驱动记录(七)像画布一样操作点阵屏

前言
      LED点阵模块也是显示屏,显示屏就不只是用来显示文字,还应该可以用来画图。

      那么一个可以绘图的显示屏应该具有哪些条件:

      1)要有一块区域做为显存,就是要显示的数据,在单片机中为了节约空间要用位来控制led亮灭。一个字节8位,可以控制8个led。16x64LED就需要16x64/8=128个字节的显存,在单片机中用数组表示。

      2)显存有了,就需要绘图函数来控制数组的值。这部分涉及的数学方面的知识,还有对位的控制,比较烧脑,超出个人能力。所以我问了AI,它给了我一段程序,见后文。

      3)下一步,就回归正题,在单片机显示。没有新花样,还是行扫描,595。


一、像画布一样操作点阵屏源码
1.main.c
#include <STC89C5xRC.H>
#include <intrins.h>
#include <absacc.h>
#include <string.h>
#include "draw.h"


sbit SH_CP = P1^5;
sbit DS = P2^7;
sbit ST_CP = P1^6;

typedef unsigned int Uint16;
typedef unsigned char Uint8;

extern unsigned char xdata canvas;

void Delay1000ms()                //@11.0592MHz
{
        unsigned char i, j, k;

        _nop_();
        i = 8;
        j = 1;
        k = 122;
        do
        {
                do
                {
                        while (--k);
                } while (--j);
        } while (--i);
}



void delayms(unsigned int m)
{
        int i,j;
        for(i=0; i<m; i++)
                for(j=0; j<120; j++);
}

//595就是串行输入,凑足8个并行输出,SH_CP上升沿管串行输入
void HC595(unsigned char dat)
{
        unsigned char j;
        for(j=0;j<8;j++)
        {
                SH_CP = 0;        //为移位准备
                DS = dat & 0x01;        //先低位
                dat=dat>>1;
                //DS=dat&0x80;
                //dat=dat<<1;
                SH_CP =1;        //上升沿,移位
        }
}




void drawbuff()
{
        unsigned char i,j,n,index;
        for(n=0;n<24;n++)    //为了字不闪
        {
                for(j=0;j<16;j++)
                {

                        for(i=0;i<8;i++)
                        {
                                        index=i+j*8;
                                        HC595(~canvas);       
                        }
                        ST_CP = 0;
                        ST_CP = 1;
                        P1=15-j;
                        Delay1000ms();
                        P1=0xff;
                }
        }
}



void testLed()
{
        unsigned char k,i,j,dat;
        for(k=0;k<16;k++)
        {
                for(i=0;i<4;i++)
                {
            HC595(0x00);
                        HC595(0x00);
                }
                ST_CP = 0;
                ST_CP = 1;
                P1=k;
                _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
                _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
                _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
                P1=0xff;
        }
       

}



void main(){
        clearScreen();
        testLed();       
        draw_rectangle(0, 0,10, 20, 1);
        draw_rectangle(2, 25,10, 31, 0);
        draw_line(10, 20, 15, 30);
        draw_point(10,48);
        draw_circle(8, 36 , 8, 0);
        while(1){
                drawbuff();
        }
}
2.绘图实现文件draw.c      下面程序是AI生成的,在windows环境下运行基本正常。移植到51上,改了一些类型定义,有部分函数不太正常了。比如画直线的draw_line函数。      另外需要注意的是,所有函数中x为纵坐标,y为横坐标。#include <stdio.h>
#include <math.h>
#include "draw.h"


unsigned char xdata canvas; // 画布,使用一个字节的位来表示一个点是否被画
// 画点
void draw_point(unsigned int x, unsigned int y) {
    unsigned int index = x * HEIGHT + y; // 计算该点在 canvas 中的下标
    canvas |= 1 << (index % 8); // 将该点的位置置为 1
}

// 擦除点
void erase_point(unsigned int x, unsigned int y) {
    unsigned int index = x * HEIGHT + y; // 计算该点在 canvas 中的下标
    canvas &= ~(1 << (index % 8)); // 将该点的位置置为 0
}

// 判断点是否被画
unsigned int is_point_drawn(unsigned int x, unsigned int y) {
    unsigned int index = x * HEIGHT + y; // 计算该点在 canvas 中的下标
    return canvas & (1 << (index % 8)); // 判断该点的位置是否为 1
}

// 画线
void draw_line(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) {
    unsigned int dx = x2 - x1; // 计算 x 轴方向的增量
    unsigned int dy = y2 - y1; // 计算 y 轴方向的增量
    unsigned int incr = 1; // 确定增量的方向
                unsigned int x,y,d;
    if (dx < 0) { // 如果 x 轴增量为负数,则将增量方向翻转
      dx = -dx;
      dy = -dy;
      incr = -1;
    }
    x = x1, y = y1; // 初始点的坐标
    d = 0; // 决策变量
    draw_point(x, y); // 先画初始点
    while (x != x2) { // 沿着 x 轴方向依次画点
      x += incr;
      d += dy;
      if (d >= dx) { // 如果决策变量超过了 dx,则需要向 y 轴方向移动一格
            y++;
            d -= dx;
      } else if (d <= -dx) { // 如果决策变量小于了 -dx,则需要向 y 轴方向移动一格
            y--;
            d += dx;
      }
      draw_point(x, y); // 画当前点
    }
}

// 画矩形
void draw_rectangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int fill) {
    unsigned int x, y;
    if (x1 > x2) { // 如果 x1 大于 x2,则交换两个点的坐标
      int temp = x1;
      x1 = x2;
      x2 = temp;
    }
    if (y1 > y2) { // 如果 y1 大于 y2,则交换两个点的坐标
      unsigned int temp = y1;
      y1 = y2;
      y2 = temp;
    }
    // 画上下两条边
    for (x = x1; x <= x2; x++) {
      draw_point(x, y1);
      draw_point(x, y2);
    }
    // 画左右两条边
    for (y = y1; y <= y2; y++) {
      draw_point(x1, y);
      draw_point(x2, y);
    }
    if (fill) { // 如果需要填充,则将矩形内部的点全部画上
      for (x = x1 + 1; x < x2; x++) {
            for (y = y1 + 1; y < y2; y++) {
                draw_point(x, y);
            }
      }
    }
}

// 画圆
void draw_circle(unsigned int x, unsigned int y, unsigned int r, unsigned int fill) {
    unsigned int dx = 0, dy = r;
    unsigned int d = 1 - r;
                unsigned i;
    draw_point(x + dx, y + dy);
    draw_point(x + dx, y - dy);
    draw_point(x + dy, y + dx);
    draw_point(x + dy, y - dx);
    while (dx < dy) {
      dx++;
      if (d < 0) {
            d = d + 2 * dx + 1;
      } else {
            dy--;
            d = d + 2 * (dx - dy) + 1;
      }
      if (fill) { // 如果需要填充,则画圆内部的点
            for (i = x - dx + 1; i < x + dx; i++) {
                draw_point(i, y + dy);
                draw_point(i, y - dy);
            }
            for (i = x - dy + 1; i < x + dy; i++) {
                draw_point(i, y + dx);
                draw_point(i, y - dx);
            }
      } else { // 否则只画圆上的点
            draw_point(x + dx, y + dy);
            draw_point(x + dx, y - dy);
            draw_point(x - dx, y + dy);
            draw_point(x - dx, y - dy);
            draw_point(x + dy, y + dx);
            draw_point(x + dy, y - dx);
            draw_point(x - dy, y + dx);
            draw_point(x - dy, y - dx);
      }
    }
}

// 画椭圆
void draw_ellipse(unsigned int x, unsigned int y, unsigned int a, unsigned int b, unsigned int fill) {
    unsigned int aa = a * a;
    unsigned int bb = b * b;
    unsigned int aa2 = 2 * aa;
    unsigned int bb2 = 2 * bb;
    unsigned int x0 = x - a;
    unsigned int x1 = x + a;
    unsigned int y0 = y - b;
    unsigned int y1 = y + b;
    unsigned int dx = a / sqrt(aa + bb);
    unsigned int dy = dx * b / a;
    unsigned int sx = x - dx;
    unsigned int ex = x + dx;
                unsigned int y2;
    if (fill) { // 填充椭圆
      for ( y2 = y0 + 1; y2 < y1; y2++) {
            int x02 = aa * (y - y0 - 1) * (y - y0 - 1); // (y-1)^2*a^2
            int x12 = aa * (y - y1) * (y - y1); // (y- y1)^2*a^2
            for (y2 = x02 / aa + x0 + 1; y2 < x1 - x12 / aa - 1; y2++) {
                draw_point(x, y);
            }
      }
    } else { // 画椭圆
      int x = 0, y = b;
      int d = bb - aa * b + aa / 4;
      while (bb2 * x < aa2 * y) {
            draw_point(x0 + x, y0 + y);
            draw_point(x1 - x, y0 + y);
            draw_point(x0 + x, y1 - y);
            draw_point(x1 - x, y1 - y);
            if (d < 0) {
                x++;
                d += bb2 * x + bb;
            } else {
                x++;
                y--;
                d += bb2 * x - aa2 * y + bb;
            }
      }
      d = bb * (x + 0.5) * (x + 0.5) + aa * (y - 1) * (y - 1) - aa * bb;
      while (y >= 0) {
            draw_point(x0 + x, y0 + y);
            draw_point(x1 - x, y0 + y);
            draw_point(x0 + x, y1 - y);
            draw_point(x1 - x, y1 - y);
            if (d > 0) {
                y--;
                d += aa - aa2 * y;
            } else {
                x++;
                y--;
                d += bb2 * x - aa2 * y + aa;
            }
      }
    }
}

// 画三角形
void draw_triangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int x3, unsigned inty3, unsigned int fill) {
    if (fill) { // 填充三角形
      int x, y, x_min, x_max, y_min, y_max;
      x_min = x1 < x2 ? (x1 < x3 ? x1 : x3) : (x2 < x3 ? x2 : x3);
      x_max = x1 > x2 ? (x1 > x3 ? x1 : x3) : (x2 > x3 ? x2 : x3);
      y_min = y1 < y2 ? (y1 < y3 ? y1 : y3) : (y2 < y3 ? y2 : y3);
      y_max = y1 > y2 ? (y1 > y3 ? y1 : y3) : (y2 > y3 ? y2 : y3);
      for (x = x_min + 1; x < x_max; x++) { // 遍历所有可能的点
            for (y = y_min + 1; y < y_max; y++) {
                if (((x2 - x1) * (y - y1) - (y2 - y1) * (x - x1)) * ((x3 - x2) * (y - y2) - (y3 - y2) * (x - x2)) * ((x1 - x3) * (y - y3) - (y1 - y3) * (x - x3)) < 0) { // 用叉积判断当前点是否在三角形内部
                  draw_point(x, y); // 如果在则画点
                }
            }
      }
    } else { // 画三角形
      draw_line(x1, y1, x2, y2);
      draw_line(x2, y2, x3, y3);
      draw_line(x3, y3, x1, y1);
    }
}

// 显示画布
void showcanvas() {
        unsigned int y ,x;
        for ( y = 0; y < HEIGHT; y++) {
        for (x = 0; x < WIDTH; x++) {
        putchar(is_point_drawn(x, y) ? '1' : '0'); // 根据点是否被画来输出 0 或 1
        } putchar('\n');
        }
}


void clearScreen(){
        unsigned i,j;
        for(i=0;i<WIDTH;i++)
        for(j=0;j<HEIGHT/8;j++)
        {
                canvas=0x00;
        }
}
3.绘图功能头文件:draw.h      关于画图的函数都在这里了:#ifndef __CANVAS_DRAW_H__
#define _CANVAS_DRAW_H__

/
//
// 画图程序 by AI,20230508
//

#define WIDTH 16 // 画布宽度
#define HEIGHT 64 // 画布高度




// 画点
void draw_point(unsigned int x, unsigned int y);

// 擦除点
void erase_point(unsigned int x, unsigned int y) ;

// 判断点是否被画
unsigned int is_point_drawn(unsigned int x, unsigned int y);

// 画线
void draw_line(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) ;

// 画矩形
void draw_rectangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int fill);

// 画圆
void draw_circle(unsigned int x, unsigned int y, unsigned int r, unsigned int fill);

// 画椭圆
void draw_ellipse(unsigned int x, unsigned int y, unsigned int a, unsigned int b, unsigned int fill) ;

// 画三角形
void draw_triangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int x3, unsigned int y3, unsigned int fill) ;

// 显示画布
void showcanvas() ;

void clearScreen();

/

#endif


神农鼎 发表于 2023-11-25 08:08:14

可以尝试下【USB直接仿真 / USB直接下载】的STC8H8K64U

www.stcai.com



页: [1]
查看完整版本: STC89C52驱动 16x64LED点阵模块驱动记录(七)像画布一样操作点阵屏