6885浏览
查看: 6885|回复: 9

FireBeetle手机可控POV显示器

[复制链接]
本帖最后由 云天 于 2022-2-13 09:49 编辑

FireBeetle手机可控POV显示器图29


FireBeetle手机可控POV显示器图14


FireBeetle手机可控POV显示器图15


之前的一个项目,当时没有做成功。用 FireBeetle BLE4.1 低功耗蓝牙控制器 、霍尔传感器、电机和一排 LED 制作的视觉暂留POV显示器。什么是POV?
人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,也被称为是“视觉暂留”。
人眼每秒处理的图像不超过 10-12 个。当一系列图像很快的连续性的滚动时,就会让我们产生幻觉,感觉画面在动。这也是制作动画电影的基本原则。
项目制作过程:
【电机】
电机是从旧录音机上拆下来的,应该是用来转动磁带的。

FireBeetle手机可控POV显示器图4


FireBeetle手机可控POV显示器图5

根据上面的标识可知:
电机额定电压9V,转速2400rpm,逆时针方向旋转(CCW),可通过改变通电正负极,改变旋转方向。


【控制电机】
利用Arduino+编码器+L289N电机驱动板来控制电机转速。FireBeetle手机可控POV显示器图6
编码器接在A0引脚,4,5号引脚接LED89N电机驱动板逻辑输入引脚,3号引脚接LED89N电机驱动板通道A使能引脚。
FireBeetle手机可控POV显示器图7
【POV控制器】
FireBeetle BLE4.1 低功耗蓝牙控制器 (Arduino Pro Mini 3.3V),支持锂电池接口:3.7 V锂电池。
FireBeetle手机可控POV显示器图1
D2——D9引脚接LED灯。
FireBeetle手机可控POV显示器图9

FireBeetle手机可控POV显示器图8
【霍尔传感器】
霍尔传感器接FireBeetle控制器的D10引脚。

FireBeetle手机可控POV显示器图13

FireBeetle手机可控POV显示器图12

调整好霍尔传感与磁铁的高度。

【安装 LED】
FireBeetle手机可控POV显示器图10
1、剪取一块洞洞板
2、安装 LED 灯,我使用八个白色的 LED 灯。
3、将所有 LED 灯的负极端口都焊接到洞洞板上。
4、添加电阻,防止 LED 灯因电压过高而烧坏。我使用 220ohm 的电阻,将每个电阻焊接到每个 LED 灯的正极。
5、将跳线电缆连接到电阻的另一个脚上。

电阻常识:
五色环电阻
五个色环电阻的识别:第一、二、三环分别代表三位有效数的阻值;第四环代表倍率;第五环代表误差。 如果第五条色环为黑色,一般用来表示为绕线电阻器,第五条色环如为白色,一般用来表示为保险丝电阻器。如果电阻体只有中间一条黑色的色环,则代表此电阻为零欧姆电阻。
例:红 红 黑 棕 金
其电阻为 220×10^1=2.2KΩ 误差为±5%
第一色环是百位数,第二色环是十位数,
第三色环是个位数,第四色环是应乘颜色次幂,
第五色环是误差率。
首先,从电阻的底端,找出代表公差精度的色环,金色的代表5%,银色的代表10%。上例中,最末端色环为金色,故误差率为5%。再从电阻的另一端,找出第一条、第二条色环,读取其相对应的数字,上例中,前三条色环都为红红黑,故其对应数字为红2、红2、黑0,其有效数是220。再读取第四条倍数色环,棕1。所以,我们得到的阻值是220×10^1=2.2KΩ。即阻值在2090-2310之间都是好的电阻。 如果第四条倍数色环为金色,则将有效数乘以0.1。如果第四条倍数色环为银色,则乘以0.01。
FireBeetle手机可控POV显示器图11

测试程序】

程序代码中进行了详细注释

FireBeetle手机可控POV显示器图2

FireBeetle手机可控POV显示器图3
显示“HELLO WORLD”
  1. int asc;
  2. int letterSpace;
  3. int delayTime;
  4. void setup() {
  5.   // initialize digital pin  output.
  6.   Serial.begin(9600);
  7.   for(unsigned char i=2;i<10;i++){//定义2-9号,共8个引脚为输出
  8.     pinMode(i, OUTPUT);
  9.   }
  10.    pinMode(10, INPUT);//10号引脚为输入,接霍尔传感器
  11.   for(unsigned char i=2;i<10;i++){//初始化8个引脚为低电平
  12.    digitalWrite(LED_BUILTIN, LOW);
  13.   }
  14.   asc=toascii('A');//获取大写字母A的Ascci值
  15.   letterSpace=4;//定义字母之间的间距(毫秒)
  16.   delayTime=500;//定义点出现的时间(微秒)
  17. }
  18. //字母十六进制点阵数组
  19. int Mychar[26][8]={
  20. {0xff,0xe7,0xe7,0xdb,0x81,0xbd,0x7e,0xff},//A
  21. {0xff,0xc1,0xbd,0xc1,0xbd,0x7d,0x81,0xff},//B
  22. {0xff,0xc3,0xbd,0xfd,0xfd,0x79,0x83,0xff},//C
  23. {0xff,0xc1,0x9d,0xbd,0xbd,0x9d,0xc1,0xff},//D
  24. {0xff,0x81,0xfd,0x81,0xfd,0xfd,0x81,0xff},//E
  25. {0xff,0x81,0xfd,0x81,0xfd,0xfd,0xfd,0xff},//F
  26. {0xff,0xc3,0xb9,0xfd,0x8d,0xb9,0x83,0xff},//G
  27. {0xff,0xbd,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//H
  28. {0xff,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//I
  29. {0xff,0xbf,0xbf,0xbf,0xbf,0xbd,0xc3,0xff},//J
  30. {0xff,0x9d,0xed,0xf5,0xe9,0xdd,0xbd,0xff},//k
  31. {0xff,0xfd,0xfd,0xfd,0xfd,0xfd,0x81,0xff},//L
  32. {0xff,0x99,0x99,0x99,0xa5,0xa5,0xa5,0xff},//M
  33. {0xff,0xb9,0xb9,0xb5,0xad,0x9d,0x9d,0xff},//N
  34. {0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff},//O
  35. {0xff,0xc1,0xbd,0xbd,0xc1,0xfd,0xfd,0xff},//P
  36. {0xff,0xc3,0x99,0xbd,0xbd,0xa9,0xc3,0xbf},//Q
  37. {0xff,0xc1,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//R
  38. {0xff,0xc3,0xbd,0xc3,0x3f,0x7d,0x83,0xff},//S
  39. {0xff,0x80,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//T
  40. {0xff,0xbd,0xbd,0xbd,0xbd,0xbd,0xc3,0xff},//U
  41. {0xff,0x7e,0xbd,0xdd,0xdb,0xe3,0xf7,0xff},//V
  42. {0xff,0xff,0xff,0x66,0xa6,0x99,0x99,0xff},//W
  43. {0xff,0xdd,0xeb,0xf7,0xe3,0xd9,0xbc,0xff},//X
  44. {0xff,0xbc,0xd9,0xe3,0xf7,0xf7,0xf7,0xff},//Y
  45. {0xff,0x80,0xdf,0xe7,0xfb,0xfd,0x80,0xff} //Z
  46. };
  47. int pinState=0;
  48. void printLetter(String s){//显示字符串函数
  49.   for(int m=0;m<s.length();m++){
  50.     char s1=s.charAt(m);    //取字符
  51.     int xh=toascii(s1)-asc;//与字符'A'的Ascii码取差,获取在数组中的位置
  52.    
  53.     for(int k=0;k<8;k++){//8*8点阵
  54.      for(int b=0;b<8;b++){
  55.        pinState=!bitRead(Mychar[xh][k],b);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
  56.        digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
  57.    }
  58.     delayMicroseconds(delayTime);//点间隔时间
  59.    }
  60.    delay(letterSpace);//字母间隔时间
  61.   }
  62. }
  63. void loop() {
  64.    boolean d=digitalRead(10);//获取霍尔传感器状态
  65.    
  66.    if(!d){//有磁时,为低电平
  67.    printLetter("HELLOWORLD");
  68.    }
  69.    
  70.   
  71. }
复制代码

【App Inventor2与FireBeetle BLE4.1低功耗蓝牙连接】
为获取POV旋转一周所需时间,花了一天时间,搞定了“App Inventor2与FireBeetle BLE4.1低功耗蓝牙连接”。

1、使用APP Inventor2 WxBit中自带的“低功耗蓝牙模块”。
FireBeetle手机可控POV显示器图16
2、获取蓝牙地址
扫描蓝牙设备,获取蓝牙地址
FireBeetle手机可控POV显示器图17
FireBeetle手机可控POV显示器图18

名称为:Bluno2
3、获取监听服务及特征UUID
UUID是“Universally Unique Identifier”的简称,通用唯一识别码的意思。对于蓝牙设备,每个服务都有通用、独立、唯一的UUID与之对应。也就是说,在同一时间、同一地点,不可能有两个相同的UUID标识的不同服务。
FireBeetle手机可控POV显示器图19
FireBeetle手机可控POV显示器图20
服务UUID有四个,FireBeetle BLE4.1低功耗蓝牙,监听服务使用“dfb0”。
FireBeetle手机可控POV显示器图21

FireBeetle手机可控POV显示器图22
特征UUID有16个,与服务“dfb0”对应的有3个,第一个为监听特征UUID(所有中第14个)。
FireBeetle手机可控POV显示器图23
收到信息后,在调试窗口显示。
FireBeetle手机可控POV显示器图24
4、FireBeetle BLE4.1低功耗蓝牙测试程序
Serial.begin(115200); 波特率一定要使用115200,我试了9600,APP Inventor2接收不到信息。
  1. char num_str[25];
  2. void setup() {
  3. Serial.begin(115200); //initial the Serial
  4. }
  5. void loop(){
  6. //if(Serial.available()){
  7. //Serial.write(Serial.read()); //send what has been received
  8. //Serial.println(); //print line feed character
  9. //}
  10.   itoa(564,num_str,10);
  11.   Serial.write(num_str);
  12.   delay(2000);
  13. }
复制代码
【获取旋转一周时间】


FireBeetle手机可控POV显示器图25
当前程序使用micros();获取系统运行时间,为微秒,如电机旋转过慢,时间数据可能会溢出。后期改成millis();为毫秒。
  1. int asc;
  2. int letterSpace;
  3. int delayTime;
  4. char num_str[25];//保存转换后时间差的数字字符串
  5. int time_pre=0;
  6. void setup() {
  7.   // initialize digital pin  output.
  8.   Serial.begin(115200);
  9.   for(unsigned char i=2;i<10;i++){//定义2-9号,共8个引脚为输出
  10.     pinMode(i, OUTPUT);
  11.   }
  12.    pinMode(10, INPUT);//10号引脚为输入,接霍尔传感器
  13.   for(unsigned char i=2;i<10;i++){//初始化8个引脚为低电平
  14.    digitalWrite(LED_BUILTIN, LOW);
  15.   }
  16.   asc=toascii('A');//获取大写字母A的Ascci值
  17.   letterSpace=4;//定义字母之间的间距(毫秒)
  18.   delayTime=500;//定义点出现的时间(微秒)
  19.   time_pre=micros();
  20. }
  21. //字母十六进制点阵数组
  22. int Mychar[26][8]={
  23. {0xff,0xe7,0xe7,0xdb,0x81,0xbd,0x7e,0xff},//A
  24. {0xff,0xc1,0xbd,0xc1,0xbd,0x7d,0x81,0xff},//B
  25. {0xff,0xc3,0xbd,0xfd,0xfd,0x79,0x83,0xff},//C
  26. {0xff,0xc1,0x9d,0xbd,0xbd,0x9d,0xc1,0xff},//D
  27. {0xff,0x81,0xfd,0x81,0xfd,0xfd,0x81,0xff},//E
  28. {0xff,0x81,0xfd,0x81,0xfd,0xfd,0xfd,0xff},//F
  29. {0xff,0xc3,0xb9,0xfd,0x8d,0xb9,0x83,0xff},//G
  30. {0xff,0xbd,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//H
  31. {0xff,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//I
  32. {0xff,0xbf,0xbf,0xbf,0xbf,0xbd,0xc3,0xff},//J
  33. {0xff,0x9d,0xed,0xf5,0xe9,0xdd,0xbd,0xff},//k
  34. {0xff,0xfd,0xfd,0xfd,0xfd,0xfd,0x81,0xff},//L
  35. {0xff,0x99,0x99,0x99,0xa5,0xa5,0xa5,0xff},//M
  36. {0xff,0xb9,0xb9,0xb5,0xad,0x9d,0x9d,0xff},//N
  37. {0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff},//O
  38. {0xff,0xc1,0xbd,0xbd,0xc1,0xfd,0xfd,0xff},//P
  39. {0xff,0xc3,0x99,0xbd,0xbd,0xa9,0xc3,0xbf},//Q
  40. {0xff,0xc1,0xbd,0x81,0xbd,0xbd,0xbd,0xff},//R
  41. {0xff,0xc3,0xbd,0xc3,0x3f,0x7d,0x83,0xff},//S
  42. {0xff,0x80,0xf7,0xf7,0xf7,0xf7,0xf7,0xff},//T
  43. {0xff,0xbd,0xbd,0xbd,0xbd,0xbd,0xc3,0xff},//U
  44. {0xff,0x7e,0xbd,0xdd,0xdb,0xe3,0xf7,0xff},//V
  45. {0xff,0xff,0xff,0x66,0xa6,0x99,0x99,0xff},//W
  46. {0xff,0xdd,0xeb,0xf7,0xe3,0xd9,0xbc,0xff},//X
  47. {0xff,0xbc,0xd9,0xe3,0xf7,0xf7,0xf7,0xff},//Y
  48. {0xff,0x80,0xdf,0xe7,0xfb,0xfd,0x80,0xff} //Z
  49. };
  50. int pinState=0;
  51. void printLetter(String s){//显示字符串函数
  52.   for(int m=0;m<s.length();m++){
  53.     char s1=s.charAt(m);    //取字符
  54.     int xh=toascii(s1)-asc;//与字符'A'的Ascii码取差,获取在数组中的位置
  55.    
  56.     for(int k=0;k<8;k++){//8*8点阵
  57.      for(int b=0;b<8;b++){
  58.        pinState=!bitRead(Mychar[xh][k],b);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
  59.        digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
  60.    }
  61.     delayMicroseconds(delayTime);//点间隔时间
  62.    }
  63.    delay(letterSpace);//字母间隔时间
  64.   }
  65. }
  66. void loop() {
  67.    boolean d=digitalRead(10);//获取霍尔传感器状态
  68.    
  69.    if(!d){//有磁时,为低电平
  70.    itoa(micros()-time_pre,num_str,10);
  71.    time_pre=micros();
  72.    Serial.write(num_str);
  73.    printLetter("HELLOWORLD");
  74.    }
  75.    
  76.   
  77. }
复制代码

【发送显示内容】
通过APP发送显示内容
FireBeetle手机可控POV显示器图26
  1. String show_str;
  2. String comdata;
复制代码
  1. void loop() {
  2.    boolean d=digitalRead(10);//获取霍尔传感器状态
  3.    while(Serial.available()>0)
  4.    {
  5.     comdata+=char(Serial.read());
  6.     delay(2);
  7.    }
  8.    if(comdata.length()>0)
  9.    {
  10.     show_str=comdata;
  11.     comdata="";
  12.    }
  13.    if(!d){//有磁时,为低电平
  14.    itoa(micros()-time_pre,num_str,10);
  15.    time_pre=millis();//micros();为微秒,millis();为毫秒
  16.    //Serial.write(num_str);
  17.    printLetter(show_str);
  18.    }
  19.    
  20.   
  21. }
复制代码
FireBeetle手机可控POV显示器图27

FireBeetle手机可控POV显示器图28

因字母编码和程序读取的顺序,显示的内容看着不太习惯。调整一下程序如下:
  1. void printLetter(String s){//显示字符串函数
  2.   for(int m=s.length()-1;m>=0;m--){
  3.     char s1=s.charAt(m);    //取字符
  4.     int xh=toascii(s1)-asc;//与字符'A'的Ascii码取差,获取在数组中的位置
  5.    
  6.     for(int k=7;k>=0;k--){//8*8点阵
  7.      for(int b=0;b<8;b++){
  8.        pinState=!bitRead(Mychar[xh][b],k);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
  9.        digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
  10.    }
  11.     delayMicroseconds(delayTime);//点间隔时间
  12.    }
  13.    delay(letterSpace);//字母间隔时间
  14.   }
  15. }
复制代码
读取顺序调整。

FireBeetle手机可控POV显示器图30


【显示时间】1、用电子表格画点阵图,再转换成16进制形式。
FireBeetle手机可控POV显示器图31
对应:0,1,2,3,4,5,6,7,8,9,:字符共11个
  1. {{0xE7,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xE7},//0
  2. {0xE7,0xF7,0xF7,0xF7,0xF7,0xF7,0xF7,0xE3},//1
  3. {0xE7,0xDB,0xFB,0xF7,0xEF,0xDF,0xDB,0xC3},//2
  4. {0xC7,0xFB,0xFB,0xC7,0xC7,0xFB,0xFB,0xC7},//3
  5. {0xF7,0xEF,0xDF,0xD7,0xC3,0xF7,0xF7,0xF7},//4
  6. {0xC3,0xDF,0xDF,0xC7,0xFB,0xFB,0xF7,0xCF},//5
  7. {0xF3,0xEF,0xDF,0xC7,0xDB,0xDB,0xDB,0xE7},//6
  8. {0xC3,0xFB,0xFB,0xFB,0xFB,0xFB,0xFB,0xFB},//7
  9. {0xE7,0xDB,0xDB,0xE7,0xE7,0xDB,0xDB,0xE7},//8
  10. {0xE7,0xDB,0xDB,0xDB,0xE3,0xFB,0xF7,0xCF},//9
  11. {0xC7,0xC7,0xC7,0xFF,0xFF,0xC7,0xC7,0xC7}};//:
复制代码
  1. void printTime(String s){//显示字符串函数
  2.   for(int m=s.length()-1;m>=0;m--){
  3.     char s1=s.charAt(m);    //取字符
  4.     int xh=0;
  5.     if(s1==':'){
  6.       xh=10;
  7.     }
  8.     else{
  9.       xh=toascii(s1)-toascii('0');//与字符'0'的Ascii码取差,获取在数组中的位置
  10.     }
  11.    
  12.     for(int k=0;k<8;k++){//8*8点阵
  13.      for(int b=0;b<8;b++){
  14.        pinState=!bitRead(Mychar[xh][b],k);//读取指定位的数据,因点阵码为暗码,读出的位要取反。
  15.        digitalWrite(b+2, pinState); //按读取的位值点亮或熄灭对应的LED
  16.    }
  17.     delayMicroseconds(delayTime);//点间隔时间
  18.    }
  19.    delay(letterSpace);//字母间隔时间
  20.   }
  21. }
复制代码
FireBeetle手机可控POV显示器图32
【显示视频】


【知识扩展】

测试小功率的LED灯珠工作电压:

1、需要使用的万用表,万用表选择二极管档。(其测试电流约1mA)

FireBeetle手机可控POV显示器图33

2、观测时,长脚为正。用表测时如图:若表有读数,则此时红表笔所测端为二极管的正极,同时发光二极管会发光。

FireBeetle手机可控POV显示器图34

3、若没有读数,则将表笔反过来再测一次;如果两次测量都没有示数,表示此发光二极管已经损坏。


汤果  初级技神

发表于 2022-2-13 06:48:58

好高级的样子
回复

使用道具 举报

云天  初级技神
 楼主|

发表于 2022-3-6 17:25:59

https://tanqin.blog.csdn.net/art ... tm_relevant_index=5
全面且简单明了的蓝牙服务及UUID介绍
回复

使用道具 举报

水晶  初级技师

发表于 2022-3-10 11:59:35

666666666666666666
回复

使用道具 举报

QQQQQQQ  初级技匠

发表于 2022-3-11 20:42:14

厉害厉害
回复

使用道具 举报

QQQQQQQ  初级技匠

发表于 2022-3-11 20:45:56

厉害厉害
回复

使用道具 举报

QQQQQQQ  初级技匠

发表于 2022-3-11 20:51:51

厉害厉害
回复

使用道具 举报

QQQQQQQ  初级技匠

发表于 2022-3-11 20:52:52

厉害厉害
回复

使用道具 举报

QQQQQQQ  初级技匠

发表于 2022-3-12 11:05:58

厉害厉害
回复

使用道具 举报

 初级技匠

发表于 2022-4-13 11:18:12


嗯,奇异博士的那玩意儿用这个同样也可以实现
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail