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

文章字数限制,分成三篇:

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

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

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


 

程序源码

  • 完整代码下载:点击下载附件
  • 主程序展示(凑个字数哈)
      1 // ----------------------------------------------------------------------------
      2 // povLaser.ino
      3 //
      4 // Created 2015-08-26
      5 // By seesea <seesea2517#gmail#com>
      6 //
      7 // 激光POV
      8 //
      9 // - 由于需要用到外部中断,又想要端口整体操作,所以用了 mega2560
     10 // - A0 - A7 为 PORTK 端口,接到八个激光二极管正极,激光二极管负极接地(原用 pin22 - pin29 PORTA 端口,但焊接电路不方便就改 PORTK 了)
     11 // - pin21 为外部中断 2,作为 POV 起点的光敏电阻检测引脚
     12 // - pin20 为外部中断 3,作为 POV 终点的光敏电阻检测引脚
     13 // - 光敏电阻一端接 VCC,另一端通过 10K 的可变电阻接到 GND,相接处引出接到 pin20 和 pin21
     14 //   通过调整可变电阻达到调整检测灵敏度的目的,以便在不同的亮度的环境下使用
     15 // - pin3 输出 PWM 通过 PNP 三极管控制电机转动,通过调整 PWM 值控制电机以合适速度转动
     16 // - 电机控制一个镜子转动,镜子反射激光,达到偏转光线的作用
     17 //
     18 // 原打算在电机轴上使用光栅和红外对管来检测起止位置,后来发现镜子的转动造成光线 2 倍于镜子的转动角偏转,不好用
     19 // 所以给发射部分增加一个激光管做为光线位置指示,用光敏电阻来检测就完全吻合了
     20 //
     21 // ----------------------------------------------------------------------------
     22 
     23 #include "asciiToDot.h"
     24 #include <avr/io.h>
     25 
     26 #define PORT_LASER  PORTK                               // 控制八个激光二极管的端口寄存器
     27 #define DDR_LASER   DDRK                                // 控制八个激光二极管的端口方向寄存器
     28 
     29 const char interruptNumStartSensor = 2;                 // 开始显示的光敏电阻的中断号
     30 const char interruptNumStopSensor  = 3;                 // 结束显示的光敏电阻的中断号
     31 volatile unsigned long ti          = 0;                 // 中断函数中用于记录时间的变量
     32 volatile unsigned long microDelay  = 0;                 // 扫描时激光二极管的点亮延时,在中断函数中更新值
     33 volatile bool flagDisplayOn        = false;             // 是否显示的标志,当开始光敏电阻检测到信号的时候置 true,结束光敏电阻检测到信号时置 false
     34 
     35 const unsigned char pinPwmMotor    = 3;                 // 电机 pwm 控制引脚号
     36 const unsigned char pwmMotor       = 150;               // 电机 pwm 值
     37 
     38 #define MAX_BUF_SIZE 50                                 // 串口接收最大字符数
     39 char bufStr[MAX_BUF_SIZE] = "seesea";                   // 默认显示字符
     40 byte *bufDot = stringToDot(bufStr);                     // 点阵缓冲区
     41 int  colNum  = strlen(bufStr) * asciiCharDotWidth;      // 点阵缓冲区大小
     42 
     43 // 开始显示的中断处理函数
     44 void ISR_displayOn()
     45 {
     46     ti = millis();
     47     flagDisplayOn = true;
     48 }
     49 
     50 // 结束显示的中断处理函数
     51 void ISR_displayOff()
     52 {
     53     microDelay = 1000 * (millis() - ti) / colNum;
     54     flagDisplayOn = false;
     55 }
     56 
     57 void setup()
     58 {
     59     DDR_LASER = 0xFF;
     60 
     61     pinMode(pinPwmMotor, OUTPUT);
     62     analogWrite(pinPwmMotor, pwmMotor);
     63 
     64     attachInterrupt(interruptNumStartSensor, ISR_displayOn,  RISING);
     65     attachInterrupt(interruptNumStopSensor,  ISR_displayOff, RISING);
     66     interrupts();
     67 
     68     Serial.begin(9600);
     69 }
     70 
     71 void loop()
     72 {
     73     int i = 0;
     74 
     75     if (! flagDisplayOn)
     76         return;
     77 
     78     for (i = 0; i < colNum; ++i)
     79     {
     80         PORT_LASER = bufDot[i];
     81         delayMicroseconds(microDelay);
     82     }
     83 }
     84 
     85 // 串口中断
     86 // 接收串口输入的字符串,更新显示缓冲区
     87 // 输入以 \n 为结束符,结束输入后更新点阵显示缓冲区从而更新显示内容
     88 // 注意:一次输入可能发生多次串口中断
     89 void serialEvent()
     90 {
     91     static int i = 0;
     92     while (Serial.available())
     93     {
     94         bufStr[i] = (char)Serial.read();
     95 
     96         if (bufStr[i] == '\n')
     97         {
     98             bufStr[i] = '\0';
     99             delete [] bufDot;
    100             bufDot = stringToDot(bufStr);
    101             colNum = strlen(bufStr) * asciiCharDotWidth;
    102             i = 0;
    103             break;
    104         }
    105 
    106         if (i >= MAX_BUF_SIZE)
    107             continue;
    108 
    109         ++i;
    110     }
    111 }

软件字库的制作

  • 没有字库芯片,咱自力更生做软件字库
  • 制作步骤如下:
  1. 整理所需要的字符:整理了从空格到波浪号这一批常用ascii字符,它们的ascii码连续,方便编写程序
  2. 选择等宽字体,并且缩到最小显示效果也不错的字体,选择了“Courier New”字体,做成 8 个像素高的图片,可以发现每一个字符都是 7 个像素宽,这样就方便程序计算每一个字符的偏移量了
    其中有一些字符的位置需要手工调整:
    • g j p q y 上移两个像素
    • 所有的符号全体上移两个像素
    • j {} 还有微调(高度缩短了一个像素)
  3. 使用字模软件取模后,做成一个数组即可
  4. 编写函数根据输入的字符取得点阵信息
    根据每个字符宽 7 个像素,即在数组中占 7 个元素,首位是空格,ascii 值为 32,从而程序很容易计算任何一个字符的点阵数据在数组中的位置为: (字符的 ascii 值 - 空格的 ascii 值) * 7,连续 7 个元素
  5. 附上像素图(请准备好放大镜哦:D)

视频欣赏

posted @ 2015-10-01 23:30  听听海看看云  阅读(529)  评论(0编辑  收藏  举报