您的位置:首页 > 其它

激光投影POV(下)(2015-10-01)

2015-10-01 23:30 471 查看
文章字数限制,分成三篇:

激光投影POV(上)(2015-10-01)

激光投影POV(中)(2015-10-01)

激光投影POV(下)(2015-10-01)

程序源码

完整代码下载:点击下载附件

主程序展示(凑个字数哈)

// ----------------------------------------------------------------------------
// povLaser.ino
//
// Created 2015-08-26
// By seesea <seesea2517#gmail#com>
//
// 激光POV
//
// - 由于需要用到外部中断,又想要端口整体操作,所以用了 mega2560
// - A0 - A7 为 PORTK 端口,接到八个激光二极管正极,激光二极管负极接地(原用 pin22 - pin29 PORTA 端口,但焊接电路不方便就改 PORTK 了)
// - pin21 为外部中断 2,作为 POV 起点的光敏电阻检测引脚
// - pin20 为外部中断 3,作为 POV 终点的光敏电阻检测引脚
// - 光敏电阻一端接 VCC,另一端通过 10K 的可变电阻接到 GND,相接处引出接到 pin20 和 pin21
//   通过调整可变电阻达到调整检测灵敏度的目的,以便在不同的亮度的环境下使用
// - pin3 输出 PWM 通过 PNP 三极管控制电机转动,通过调整 PWM 值控制电机以合适速度转动
// - 电机控制一个镜子转动,镜子反射激光,达到偏转光线的作用
//
// 原打算在电机轴上使用光栅和红外对管来检测起止位置,后来发现镜子的转动造成光线 2 倍于镜子的转动角偏转,不好用
// 所以给发射部分增加一个激光管做为光线位置指示,用光敏电阻来检测就完全吻合了
//
// ----------------------------------------------------------------------------

#include "asciiToDot.h"
#include <avr/io.h>

#define PORT_LASER  PORTK                               // 控制八个激光二极管的端口寄存器
#define DDR_LASER   DDRK                                // 控制八个激光二极管的端口方向寄存器

const char interruptNumStartSensor = 2;                 // 开始显示的光敏电阻的中断号
const char interruptNumStopSensor  = 3;                 // 结束显示的光敏电阻的中断号
volatile unsigned long ti          = 0;                 // 中断函数中用于记录时间的变量
volatile unsigned long microDelay  = 0;                 // 扫描时激光二极管的点亮延时,在中断函数中更新值
volatile bool flagDisplayOn        = false;             // 是否显示的标志,当开始光敏电阻检测到信号的时候置 true,结束光敏电阻检测到信号时置 false

const unsigned char pinPwmMotor    = 3;                 // 电机 pwm 控制引脚号
const unsigned char pwmMotor       = 150;               // 电机 pwm 值

#define MAX_BUF_SIZE 50                                 // 串口接收最大字符数
char bufStr[MAX_BUF_SIZE] = "seesea";                   // 默认显示字符
byte *bufDot = stringToDot(bufStr);                     // 点阵缓冲区
int  colNum  = strlen(bufStr) * asciiCharDotWidth;      // 点阵缓冲区大小

// 开始显示的中断处理函数
void ISR_displayOn()
{
ti = millis();
flagDisplayOn = true;
}

// 结束显示的中断处理函数
void ISR_displayOff()
{
microDelay = 1000 * (millis() - ti) / colNum;
flagDisplayOn = false;
}

void setup()
{
DDR_LASER = 0xFF;

pinMode(pinPwmMotor, OUTPUT);
analogWrite(pinPwmMotor, pwmMotor);

attachInterrupt(interruptNumStartSensor, ISR_displayOn,  RISING);
attachInterrupt(interruptNumStopSensor,  ISR_displayOff, RISING);
interrupts();

Serial.begin(9600);
}

void loop()
{
int i = 0;

if (! flagDisplayOn)
return;

for (i = 0; i < colNum; ++i)
{
PORT_LASER = bufDot[i];
delayMicroseconds(microDelay);
}
}

// 串口中断
// 接收串口输入的字符串,更新显示缓冲区
// 输入以 \n 为结束符,结束输入后更新点阵显示缓冲区从而更新显示内容
// 注意:一次输入可能发生多次串口中断
void serialEvent()
{
static int i = 0;
while (Serial.available())
{
bufStr[i] = (char)Serial.read();

if (bufStr[i] == '\n')
{
bufStr[i] = '\0';
delete [] bufDot;
bufDot = stringToDot(bufStr);
colNum = strlen(bufStr) * asciiCharDotWidth;
i = 0;
break;
}

if (i >= MAX_BUF_SIZE)
continue;

++i;
}
}


软件字库的制作

没有字库芯片,咱自力更生做软件字库

制作步骤如下:

整理所需要的字符:整理了从空格到波浪号这一批常用ascii字符,它们的ascii码连续,方便编写程序

选择等宽字体,并且缩到最小显示效果也不错的字体,选择了“Courier New”字体,做成 8 个像素高的图片,可以发现每一个字符都是 7 个像素宽,这样就方便程序计算每一个字符的偏移量了
其中有一些字符的位置需要手工调整:

g j p q y 上移两个像素

所有的符号全体上移两个像素

j {} 还有微调(高度缩短了一个像素)

使用字模软件取模后,做成一个数组即可

编写函数根据输入的字符取得点阵信息
根据每个字符宽 7 个像素,即在数组中占 7 个元素,首位是空格,ascii 值为 32,从而程序很容易计算任何一个字符的点阵数据在数组中的位置为: (字符的 ascii 值 - 空格的 ascii 值) * 7,连续 7 个元素

附上像素图(请准备好放大镜哦:D)



视频欣赏

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: