前言
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[WIDTH * HEIGHT / 8];
-
- 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[index]);
- }
- 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[WIDTH * HEIGHT / 8]; // 画布,使用一个字节的位来表示一个点是否被画
- // 画点
- void draw_point(unsigned int x, unsigned int y) {
- unsigned int index = x * HEIGHT + y; // 计算该点在 canvas 中的下标
- canvas[index / 8] |= 1 << (index % 8); // 将该点的位置置为 1
- }
-
- // 擦除点
- void erase_point(unsigned int x, unsigned int y) {
- unsigned int index = x * HEIGHT + y; // 计算该点在 canvas 中的下标
- canvas[index / 8] &= ~(1 << (index % 8)); // 将该点的位置置为 0
- }
-
- // 判断点是否被画
- unsigned int is_point_drawn(unsigned int x, unsigned int y) {
- unsigned int index = x * HEIGHT + y; // 计算该点在 canvas 中的下标
- return canvas[index / 8] & (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 int y3, 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[i*8+j]=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
复制代码
|