Arduino论坛
直播中

余华

12年用户 427经验值
擅长:可编程逻辑 嵌入式技术 处理器/DSP
私信 关注
[经验]

点阵手写屏

` 本帖最后由 goyuqinghua 于 2014-10-24 16:53 编辑

一、目标
实现用笔在点阵屏上写字。

二、电路分析
点阵手写屏主要由三个部分组成:行扫描电路、列扫描电路、光笔检测电路,原理框图如下图所示。这里所使用的光笔里面安装了一个光敏传感器,用来检测点阵上点的状态。
分析如下:
1、 行扫描
依次使译码器的Y0、Y1……有效,而74ls138输出有效时是低电平,而我们用的点阵屏是共阳的,因此需要把它取反,本设计使用PNP三极管取反。每8个列扫描进行一次行扫描。
2、 列扫描
依次使译码器的Y0、Y1……有效,并且在OE1上输入PWM信号,当OE1为高电平时Y0~Y7电平全为高电平,当OE1为低电平时,译码器的输出由ABC三个引脚来决定。当选中某一行时我们依次选中Y0、Y1……,并且每个LED灯的亮度由OE1调节。
3、 光笔
把比较器反相输入端的参考电压设置在某一个值,并且在当光笔接收到光时同相输入端的输入电压会小于这个值,当光笔没有接收到光时同相输入端的输入电压会大于这个值。当光笔没有接收到光时通过R3的电流非常小,因此输入到比较器的同相输入端的电压会接近电源电压,此时比较器输出高电平,当光笔接收到光时通过R3的电流会增大,则R3两端的电压会增大,因此输入到比较器同相输入端的电压会减小,此时比较器输出低电平,这时单片机可以捕获这个变化然后做相应的处理。
三、程序分析
要想检测到点阵上的点我们必须点亮点阵上的LED,并且使它处于微亮状态,当光笔触碰到某个点时,就把这个点设置为高亮。那么我们如何知道要点亮的这个点的坐标呢?原理是这样的:让LED逐个点亮,首先第一行第一个,然后第一行第二个,……,到第一行最后一个时再转到第二行第一个,LED这样循环的依次点亮,并且每一个LED设置一个状态值,比如这个值为0时代表微亮,为1时代表高亮。当光笔触碰到某个点时若还没有轮到这个LED点亮,由于光笔没有检测到光因此光笔的输出保持高电平,当轮到点亮这个LED点亮时由于光笔检测到了LED发出的光,因此光笔的输出由高电平变为低电平,当单片机检测到这个电平变化时就进入中断程序,并且获取当前的行、列值,并通过这个行、列值修改这个点的状态值为1,让下一次轮到这个点点亮时就通过判断这个状态值设置为高亮状.态。当把扫描速度加快时我们就看到是整个点阵屏都是亮起来的了,而不是一个一个点亮了。

参考程序如下
#include

#define COL 1
#define ROW 2

int col = 0;                   //标记当前扫描到的行
int row = 0;                  //标记当前扫描到的列
int ledState[8][8];          //标记当前LED的状态

/* haveUpdate用于消除光笔输入的抖动,当获取到的光笔信号有下降沿的时候就更新数据,*并且把这个标志设置为1表示已经更新了数据,然后打开定时器定时,在下一次抖动的时
*候由于已经更新了数据从而忽略抖动,当定时时间到的时候重新设置这个标志位的值为0
*/
int haveUpdate =0;              
void setup()
{
  memset(ledState, 0, sizeof(ledState));             //把所有状态值都清零
initPort();                                                    //初始化端口
TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);  
TCCR2B = _BV(WGM22) | _BV(CS20);       //不分频
OCR2A = 100;  
OCR2B = 99;                                              //占空比为99%
attachInterrupt(0, externInterrupt, FALLING );//设置外部中断为下降沿中断

cli();                                              //关闭所有中断
  TCCR1A=0;                                  //寄存器A是配置PWM的,这里我们只是使用定时功能  
TCCR1B=(1<
  TCNT1=0xFE79;                            //计数器初值,25ms定时
tiMSK1 = 0;                                 //关闭溢出中断
sei();                                             //开全局中断
}

ISR(TIMER1_OVF_vect){                  //定时器中断服务程序
TIMSK1 = 0;                                 //关闭溢出中断使能
haveUpdate = 0;                             //重置标志
}


void loop()
{
scan();                                           //循环扫描
}

void externInterrupt()                         //外部中断服务程序
{
if(haveUpdate == 0){
   ledState[row][col] = 1;                //根据行和列标志当前检测到的点的状态
    haveUpdate = 1;
TCNT1=0xFE79;                        //计数器初值,25ms定时
TIMSK1 =(1<
  }
}

void initPort()
{
  inti, startPin = 4;

for(i=0; i<10; i++){
   pinMode(startPin+i, OUTPUT);
  }
setData(COL, 0x00);
setData(ROW, 0x00);//设置行和列都为0

pinMode(3, OUTPUT);   
}

void setData(int flag, int data)
{
  inti, startPin;

if(COL == flag){
   startPin = 4;
}else{
   startPin = 7;
  }
for(i=0; i<3; i++){
   digitalWrite(startPin+i, (data & (1<
  }
}

void scan()
{
for(row=0; row<8; row++){
   setData(ROW, row);
   for(col=0; col<8; col++){
     setData(COL, col);
     if(ledState[row][col] > 0)
       OCR2B = 1;
     else
       OCR2B = 98;
     delayMicroseconds(300);
     OCR2B = 98;
    }
  }
}


` clip_image002.jpg

回帖(7)

bill_yu

2014-10-31 13:51:33
挺 好玩的,我在哪可以买到模块试一下啊?
举报

打不起的小强

2016-4-25 23:23:39
请问一下_BV()语句是什么意思啊?
举报

乐方天际

2016-7-23 15:51:03
楼主做的好高端呀,我看到点阵完全没想到居然还可以作为手写屏。厉害,厉害,厉害!!!
举报

李磊

2016-8-1 16:05:37
可不可以进一步把笔去掉啊?这样太高端啦!!
举报

更多回帖

×
20
完善资料,
赚取积分