前言 参加社区比赛的制作,实在想不出什么点子,就做一个摇摇棒的激光版本吧。
电路原理
制作步骤- 先看运行效果
打个招呼:Hi, I'm seesea : )
- 做支架
感谢奥松机器人提供的套件。
- 激光管组合
原以为共阳的方便使用,后来发现不能用ULN2803驱动,但木已成舟。在面包板测试的图中可以看到ULN2003(没有2803只使用七个灯测试)用了上拉电阻,可是电阻太大带不动电阻太小了发烫,只好用三极管来驱动。所以最终的电路图里修正为共阴的接线方式。
难 点在于每一个激光管的方向对齐,不然略远一点距离就会偏得很明显,真是应了成语:差之毫厘,谬以千里。调整很累:先整体用热熔胶粘好,然后点亮激光管,利 用胶枪头的热一个一个激光管化开慢慢调整位置,调整好一个后冷却定型后再调整下一个,而调整后一个的时候还会影响到前一个,所以特别难。没办法呀,最原始 的木作加美工“裁纸刀”呀……会3D的话同学用3D打印应该精度好一些。
调整过程要盯着激光点看,很伤眼,提醒大家注意护眼!
经过好几晚的调整终于弄平整了,激光管可能受热有损坏,有一些亮度降了很多,最后的效果图可以看到中间几个亮度明显暗了,只是没有更换激光管的欲望了,这可真是苦差事。
裁纸刀切三合板:
切成这样已经很不错了:
试试大小:
共阴焊接:
焊好:
- 安装上激光管组合
- 制作反光镜
塑料片贴反光膜做的反光镜效果不好,太软会变形,反光后激光散了不平行,如果有条形的真正的镜子就好了。找半天发现一些树莓派坏外壳的亚克力挺平整的,救星啊。
超市送的山寨扇子,废物利用吧。
裁好:
反光膜,六块包邮:
做好:
还是要硬点的,有坏的树莓派亚克力外壳:
没工具,用手折:
- 安装反光镜
- 安装光敏电阻
如果有合适的机械结构是要做成活动可以调整的,这样可以控制显示范围,可以根据显示内容的长短做调整。
- 搭电路接起来
原来使用 UNO,但为了方便成组操作8个激光管,需要使用一个整个端口,UNO 只有一个完整的端口 pin0 - pin7 => PORTD,但是又要用到两个外部中断,所以要请来 Mega2560 了……
- 焊接功能板
面包板测试完后,焊接电路固定好。
- 整机安装好
- 运行:亮灯和关灯效果
默认显示LOGO:seesea,有点自恋啊。
- 支持串口改字
串口支持了,就相当于支持了蓝牙和 wifi 等各种便捷接口啦。
ArduinoCN:
程序源码- 完整代码下载:点击下载附件
- 主程序展示(凑个字数哈)
[mw_shl_code=cpp,true]
// ----------------------------------------------------------------------------
// 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;
delayMicroseconds(microDelay);
}
}
// 串口中断
// 接收串口输入的字符串,更新显示缓冲区
// 输入以 \n 为结束符,结束输入后更新点阵显示缓冲区从而更新显示内容
// 注意:一次输入可能发生多次串口中断
void serialEvent()
{
static int i = 0;
while (Serial.available())
{
bufStr = (char)Serial.read();
if (bufStr == '\n')
{
bufStr = '\0';
delete [] bufDot;
bufDot = stringToDot(bufStr);
colNum = strlen(bufStr) * asciiCharDotWidth;
i = 0;
break;
}
if (i >= MAX_BUF_SIZE)
continue;
++i;
}
}
[/mw_shl_code]
软件字库的制作- 整理所需要的字符:整理了从空格到波浪号这一批常用ascii字符,它们的ascii码连续,方便编写程序
- 选择等宽字体,并且缩到最小显示效果也不错的字体,选择了“Courier New”字体,做成 8 个像素高的图片,可以发现每一个字符都是 7 个像素宽,这样就方便程序计算每一个字符的偏移量了
其中有一些字符的位置需要手工调整:
- g j p q y 上移两个像素
- 所有的符号全体上移两个像素
- j {} 还有微调(高度缩短了一个像素)
- 使用字模软件取模后,做成一个数组即可
- 编写函数根据输入的字符取得点阵信息
根据每个字符宽 7 个像素,即在数组中占 7 个元素,首位是空格,ascii 值为 32,从而程序很容易计算任何一个字符的点阵数据在数组中的位置为: (字符的 ascii 值 - 空格的 ascii 值) * 7,连续 7 个元素 - 附上像素图(请准备好放大镜哦:D)
视频欣赏
|