3753|7

4

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

pyboardCN V2 试用笔记(二) [复制链接]

  本帖最后由 凡人就行 于 2018-9-10 20:31 编辑

pyboardCN V2 试用笔记(二)

利用定时器+OLED播放Bad apple
一.前言

        前段时间较为忙碌,一直未来得及学习交流。这次利用pyboardCN V2自带的SD卡来存储图片解码文件,通过定时器读取像素点信息文件并通过串口将信息发送给显示部分来播放B站比较火的Bad apple。
        本来打算直接利用板子的I2C通信播放图片。但是官方的SSD1306库不是很完善,播放帧数略微感人。当然本人使用刷屏的方式也确实不太合理导致延迟严重。故使用外置的stm32f103来进行专门的图片播放,来减缓
pyboard的压力。

二.工具

        一款舒服的文本编辑器,如notepad++。
        视频截取工具。如Potplayer。
        图片转换器,将MP4转换为2位像素图。可以用ps进行批处理操作,也可以用专门软件进行转换。本文使用pconverter。下载链接:http://www.zxt2007.com/imageconverter.html
        图片取模软件。可以用OLED附赠的资料,但是手动截图有些麻烦。本人参考他人设计,利用matlab进行图片取模。参考文章:https://blog.csdn.net/howlclat/article/details/50668521
        MDK,给播放设备编程。

        注:实际上本设备分为2部分,pyboardCN负责读取文件,定时串口发送,播放设备负责将串口接收到的图片码显示到OLED上。两个设备可以完全分开独立设计,互不影响。

三.图片处理

        关于OLED显示一帧帧图片播放视频,大体思路如下:
        首先,找到视频源,一般为MP4文件。由于使用黑白OLED,所选取的视频尽量颜色少,最好也为黑白视频,因此选择Bad apple是较为合适的方案。
        第二步,截取视频,将图像进行转换。图像转换有如下要求:
                (1)黑白颜色
                (2)图像大小 128*64
                (3)图像格式BMP        先用potplayer截取。
        在
pconverter中设置如下图所示:

        第三步,根据自己的刷屏方式,选择合适的图片取模方式。本人刚开始根据上文提到的参考作者的方案,进行横向8个像素点拼接成一位,如此调用pixel函数描点,结果由于循环次数较多,运算较为复杂,对micropython来说处理较慢,效果并不好。后来参考汉子显示的思路,利用OLED购买提供的例程直接显示图片,效果好很多。在pyboard里移植下这个函数应该也会快一些,不过由于本身在跑micropython,本人直接改用其他设备进行播放。这里虽然没能在pyboard上流畅播放,但是也是受了很大启发,感谢两位大大
https://bbs.eeworld.com.cn/forum.php?mod=viewthread&tid=528783&typeid=426
https://www.cnblogs.com/xxosu/p/7602859.html
        *程序中已将I2C直接驱动OLED部分注释掉了,同时刷屏方式也改了。可以参考注释掉的思路进行图片播放。程序见下文。
        因此,这里的刷屏设置如下:
        matlab程序如下,注意由于图片处理的软件有bug,图像整体右移了一列,因此要先左移回来再进行处理。
  1. clc;clear;
  2. P = 'D:\pic-apple\';
  3. D = dir([P '*.bmp']);
  4. for i = 1 : length(D)
  5.    
  6.         raw_data = imread([P D(i).name]);
  7.      %软件有bug,第1列错位了
  8.     c = [raw_data(:,2:end) raw_data(:,1)];
  9.     raw_data = c;
  10.     raw_data = double(raw_data');
  11.    
  12.     for j = 1:1024                                                % 128*64=8192=8*1024
  13.         byte = 0;
  14.             for k = 1:8                                        % 每个字节的处理,移位实现
  15.                 byte = byte + bitshift(raw_data(mod((j-1),128)+1,k+floor((j-1)/128)*8),k-1);
  16.             end
  17.         B(j) = byte;
  18.     end
  19.     newName=sprintf('%d.bin', i);                  % 构造文件名
  20.     fid = fopen(newName,'wb');
  21.     fwrite(fid,B,'uint8');
  22.     fclose(fid);
  23. end
复制代码


四.py程序

        程序主要用到文件操作和定时器的使用以及串口通信。源码如下:
  1. # main.py -- put your code here!
  2. from machine import I2C
  3. from ssd1306 import SSD1306_I2C
  4. from pyb import UART,Timer
  5. import pyb
  6. import micropython
  7. micropython.alloc_emergency_exception_buf(100)
  8. #i2c=machine.I2C(-1, sda=machine.Pin("X10"), scl=machine.Pin("X9"), freq=400000)  
  9. u1 = UART(1, 921600)
  10. u1.write('ok')
  11. pyb.delay(1000)
  12. pyb.delay(1000)


  13. num=[]
  14. flag = 0
  15. i = 1
  16. #for i in range(1,342):
  17. filename="pic/"+str(i)+".bin"
  18. f = open(filename,"rb")
  19. num = f.read()
  20.         #pyb.delay(36)

  21. def f2(t):
  22.         global num
  23.         global u1
  24.         global i
  25.         global flag
  26.         u1.write(num)
  27.         i = i+1
  28.         flag = 1
  29.         #filename="pic/"+str(i)+".bin"
  30.         #f = open(filename,"rb")
  31.         #num = f.read()

  32. tm=Timer(4, freq=10, callback=f2)

  33. while True:
  34.         if flag == 1:
  35.                 filename="pic/"+str(i)+".bin"
  36.                 f = open(filename,"rb")
  37.                 num = f.read()
  38.                 u1.write(num)
  39.                 flag = 0
复制代码


        首先是文件操作。类似C语言,直接打开文件,读取,存到数组。比C方便的是在python中有类似C++的字符串拼接操作,循环简单。
        其次是串口,直接write即可。不过在用之前需要确定波特率。这里为了速度用了921600
        最关键的还是定时器。还是这位大大的文章,帮了不小的忙。链接:https://bbs.eeworld.com.cn/thread-485618-1-1.html        关于定时器的使用,大大说的很清楚了,文档里也有示例。这里主要记录下出现的问题。
        (1)输出调试中的错误。在调试过程中可能会有内存、堆栈等问题,导致程序直接崩掉。我们需要分配中断异常处理的堆栈,才能正常输出我们的问题。
  1. import micropython
  2. micropython.alloc_emergency_exception_buf(100)
复制代码

        (2)定时器使用回调函数方式,因此不能直接在定时器中断里分配内存。例如本程序里涉及到文件操作,对文件进行打开、处理等操作,无法在回调函数中进行。程序中对回调函数做了一个信号量控制主循环文件读取使能以及串口发送。外部变量要用global声明。
  1. def f2(t):
  2.         global num
  3.         global u1
  4.         global i
  5.         global flag
  6.         u1.write(num)
  7.         i = i+1
  8.         flag = 1
  9.         #filename="pic/"+str(i)+".bin"
  10.         #f = open(filename,"rb")
  11.         #num = f.read()

  12. tm=Timer(4, freq=10, callback=f2)
复制代码


五.stm32程序

        stm32端主要是一个串口中断,贴上代码,暂不详细展开。
  1. #define MAX_LENGTH      4096
  2. volatile char receiveBuffer[MAX_LENGTH];
  3. volatile uint16_t receiveLength = 0;
  4. volatile uint8_t rxFlag = 0;  
  5. unsigned int Times = 1;
  6. static unsigned char str[MAX_LENGTH + 1];  

  7. unsigned char* USART_GetString(void)  
  8. {  
  9.     uint16_t temp;  
  10.     while(!rxFlag);     //等待接收数据,在串口接收到一帧数据时 rxFlag 将会被置 1 (USART3 中断服务函数中)  
  11.     while(rxFlag)  
  12.     {  
  13.         temp = receiveLength;  
  14.         delay_us(8);      //等待 500us   
  15.         if(temp == receiveLength)   //判断 receiveLength 是否发生变化(USART3 中断函数中 receiveLength 会有变化),如果没有,证明已经收完所有的数据,否则等待接收完成  
  16.         {  
  17.             rxFlag = 0;  
  18.         }  
  19.     }  
  20.   
  21. //    for(temp = 0; temp < receiveLength; temp++)  
  22. //    {  
  23. //        str[temp] = receiveBuffer[temp];  
  24. //    }  
  25.   
  26.     receiveLength = 0;  
  27.     //show
  28.                 OLED_DrawBMP(0,0,128,8,(unsigned char*)receiveBuffer);
  29.     return str;  
  30. }

  31. void USART1_IRQHandler(void)
  32. {  
  33.     char temp;  
  34.   
  35.     if(USART_GetITStatus(USART1, USART_IT_RXNE))  
  36.     {  
  37.         USART_ClearITPendingBit(USART1, USART_IT_RXNE);  
  38.         temp = USART_ReceiveData(USART1);
  39.         if(receiveLength == MAX_LENGTH)  
  40.         {  
  41.             return;  
  42.         }  
  43.         if(!rxFlag)  
  44.         {  
  45.             rxFlag = 1;  
  46.         }  
  47.   
  48.         receiveBuffer[receiveLength++] = temp;  
  49.     }  
  50. }
复制代码


六.效果

        将串口连接好,py以及生成的图片码bin文件放到SD卡中,播放。视频:
链接已隐藏,如需查看请登录或者注册


        由于速度问题,视频截取频率为10Hz,即100ms一张。最高能到13Hz,需要改一下定时器数值。
附件:
链接:
链接已隐藏,如需查看请登录或者注册
密码:g1c5









最新回复

Thanks a lot~  详情 回复 发表于 2018-9-14 17:39

赞赏

1

查看全部赞赏

点赞 关注
 
 

回复
举报

1万

帖子

24

TA的资源

版主

沙发
 
 
 

回复

1903

帖子

0

TA的资源

版主

板凳
 
还没开始研究
 
 
 

回复

1368

帖子

6

TA的资源

版主

4
 
厉害,学习了,谢谢分享!

点评

就等你发帖了,不能只看不练啊。  详情 回复 发表于 2018-9-12 10:34
个人签名专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
 
 
 

回复

1万

帖子

24

TA的资源

版主

5
 
懒猫爱飞 发表于 2018-9-12 08:33
厉害,学习了,谢谢分享!

就等你发帖了,不能只看不练啊。

点评

还没有板子去练手  详情 回复 发表于 2018-9-12 15:37
 
 
 

回复

1368

帖子

6

TA的资源

版主

6
 
dcexpert 发表于 2018-9-12 10:34
就等你发帖了,不能只看不练啊。

还没有板子去练手

点评

我这里有  详情 回复 发表于 2018-9-12 16:18
个人签名专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
 
 
 

回复

1万

帖子

24

TA的资源

版主

7
 

我这里有

 
 
 

回复

19

帖子

0

TA的资源

一粒金砂(初级)

8
 
Thanks a lot~
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表