百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

基于51单片机的简易“视频播放器”

bigegpt 2024-12-15 11:26 4 浏览

前言

大概10年前,我在咱们EEWORLD论坛看到有人分享了用单片机在液晶屏上播放bad apple视频,当时觉得很有趣,但是奈何当时刚入门单片机,很多都不太懂,自己想做却不会做,只能作罢。如今,工作也好几年了,也不做嵌入式这一行。前段时间,趁着周末,工作也不太忙,突然想起这个事情了,于是从网上买了元器件,自己动手再实现这个功能,以弥补自己当年的遗憾,哈哈。


介绍

本文介绍在51单片机上,使用OLED12864(SSD1306)播放视频,并且使用蜂鸣器播放音乐。因为是gif的原因,看着会比较卡,实际上是不会有卡顿的,实际效果可以点击阅读原文看视频

最终的效果如下:

播放bad apple视频:



播放数码宝贝的视频:



使用到的主要元器件如下:

  • 国产51单片机:STC15F2K60S2
  • OLED显示屏:SSD1306,分辨率为128*64
  • 无源蜂鸣器,8550三极管等


原理图如下:



具体方案

由于视频文件比较大(MB级别),而51单片机的flash一般都比较小(KB级别),因此把视频文件直接存储在单片机内部显然是不行的。可以把视频文件存储在SD卡里面,然后单片机读取SD卡里面的内容;或者视频文件直接存储在电脑上,然后电脑通过串口实时发送视频数据给单片机,单片机实时显示视频画面。文本采用后者的方案。

OLED12864绘图

我们购买OLED12864(SSD1306)显示屏时,一般卖家都会提供51单片机的示例代码,或者网上也能找到很多相关的代码。


使用这些代码在整个屏幕上绘图时,发现刷新率比较低,在11.0592M时钟频率的情况下,实测大概只有8.6fps。测试方法如下:


void main(){	for(;;)	{		p27 = ~p27;		oled_drawbmp(pic);	}}


oled_drawbmp为卖家提供的绘图的函数,每次屏幕刷新一次,p27 IO口翻转一次。使用逻辑分析仪测试p27的电平变化如下,可以看到频率约为4.3Hz,那么屏幕的刷新率大概为8.6Hz(fps)。



因为我们的目的是使用单片机在这款显示屏上播放视频,而一般视频的帧率需要大于25fps,帧率过低就会有卡顿的感觉。显然,上面提到屏幕8.6Hz的刷新率是比较低的,因此我们需要做一些优化。


比较直观且容易的优化方式之一,就是提高时钟频率,把11.0592M提高到24M或者27M的时钟频率。


第二个优化方法就是优化绘图函数。先来看看iic的开始信号和结束信号的代码:


void iic_start() //开始iic{	scl = 1;	sda = 1;	delay_5us();	sda = 0;	delay_5us();	scl = 0;}void iic_stop() //停止iic{	scl = 0;	sda = 0;	scl = 1;	delay_5us();	sda = 1;	delay_5us();}


可以看到,里面有一些延时5微妙(delay_5us),其实这个不是必须的,去掉这个延时,iic同样可以正常通信。因此,去掉这个延时,可以加快显示屏的刷新速率。


其次,我们再看看卖家提供绘图部分的函数:


/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y)*****************/void oled_drawbmp(unsigned char bmp[]) //画图{	unsigned int j = 0;	unsigned char x, y;	for (y = 0; y < 8; y++)	{		oled_set_pos(0,  y);		for (x = 0; x < 128; x++)		{			iic_writedata(bmp[j++]);		}	}}


而iic_writedata的实现如下:


void iic_writedata(unsigned char iic_data) //写数据{iic_start();	write_byte(0x78);	write_byte(0x40);	write_byte(iic_data);	iic_stop();}


可以看到,每写一次图像数据bmp[j],都会有一次iic开始与结束动作,也都会先发送两个控制指令(0x78, 0x40),这其实没有必要,优化后的函数如下:


// 快速绘制图像void oled_drawbmp_fast(unsigned char BMP[]){	unsigned int j = 0;	unsigned char x, y;	for (y = 0; y < 8; y++)	{		oled_set_pos(0, y);		iic_start();		write_byte(0x78);		write_byte(0x40);		for (x = 0; x < 128; x++)		{			write_byte(BMP[j++]);		}		iic_stop();	}}


可以看到,上面的函数减少了启动iic、结束iic,减少了写控制命令(0x78, 0x40)。使用跟之前同样的测试方法,经过上述的优化,最终屏幕的刷新率如下:



上述的结果为,使用27M时钟频率,加上上面提到的几点优化,可以看出最终屏幕的刷新率约为34.5*2=69Hz(fps),这已经满足我们播放视频所需的屏幕刷新率了。


另外,再提一点,其实还可以进一步优化,使得屏幕刷新率达到100fps以上,测试结果如下。



视频转码成十六进制格式

单片机播放视频,我们需要将视频转码为单片机可以读取的十六进制数据。

首先我们需要将视频分解为一帧一帧的图像,然后可以用如下的取模软件获得图像的十六进制字模。


但是,由于视频的帧数比较多,我们一帧一帧手动的使用取模软件获取字模,显然是一个比较累的活。因此,我们可以写个python代码,批量生成每帧画面的十六进制数据。python代码如下:


import cv2
def bit2num(pixcels):   output_val = 0   for i, pix in enumerate(pixcels):      if pix > 128: # 白色         output_val += pow(2, i)   return output_val
def main():   video_path = 'BadApple.flv'   cap = cv2.VideoCapture(video_path) # 打开视频   cnt = 0   fout = open('bad_apple_data.txt', 'w') #   while True:      ret, frame = cap.read()       # 一帧一帧的读取      if not ret:         break      cnt += 1      frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转换为灰度的画面      frame = cv2.resize(frame, (128, 64))  # 图像尺寸调整到128*64大小      convert_val = []      for row in range(0, 8):   # page0 ~ page7         for col in range(0, 128): # seg0 ~ seg127            cur_data = frame[row*8: row*8+8, col] # 取出对应的8个像素点            convert_val.append(str(bit2num(cur_data))) # 转换成8位的数据      fout.write("%s\n"%(','.join(convert_val)))
      # cv2.imshow("capture", frame)  #显示画面      # if cv2.waitKey(30) & 0xff == ord('q'): #按q退出      #    break
main()


代码比较简单,其中会调用OpenCV的库,用来读取视频以及视频画面对应的像素值。

串口发送

视频数据准备好之后,我们需要把视频数据通过串口发送给单片机,单片机接收到完整的一帧数据(一帧画面)之后,就可以开始显示画面。我们同样可以写个python代码来将视频数据发送给单片机,代码如下:


import serial  # 导入串口相关的库from time import sleep
def get_bmp_data():    filepath = 'bad_apple_data.txt'    f = open(filepath)    bmp_data = []    for line in f:        val = line.strip().split(',')        if len(val) == 0:            continue        bmp_data.append([int(x) for x in val])    f.close()    return bmp_data
def main():    com = serial.Serial('com10', 345600, timeout=10) # 设置端口号,波特率,超时时间    if not com.isOpen():  # 判断端口是否打开成功        raise "端口打开失败"
    bmp_data = get_bmp_data() # 读取刚刚生成的TXT文件    for frame in bmp_data: # 一帧一帧的发送数据        ret = com.write(bytes(frame)) # 将数据转换成二进制后发送        sleep(0.03) # 延时适当时间
main()


代码比较简单,其中会调用串口相关的serial库,然后每次循环发送一帧数据,直至全部发送完成。

小结

最后,总结一下 在51单片机播放视频的大致流程:

  1. 视频解码成一帧帧的图像,然后再转码成显示屏可以显示的十六进制格式(这一步可以提前完成)
  2. 电脑通过串口把十六进制格式的视频数据发送给单片机
  3. 单片机接收到完整的一帧数据(一幅图像)后,显示屏开始显示画面
  4. 同时,蜂鸣器播放音乐(可选)。

相关推荐

了解Linux目录,那你就了解了一半的Linux系统

大到公司或者社群再小到个人要利用Linux来开发产品的人实在是多如牛毛,每个人都用自己的标准来配置文件或者设置目录,那么未来的Linux则就是一团乱麻,也对管理造成许多麻烦。后来,就有所谓的FHS(F...

Linux命令,这些操作要注意!(linux命令?)

刚玩Linux的人总觉得自己在演黑客电影,直到手滑输错命令把公司服务器删库,这才发现命令行根本不是随便乱用的,而是“生死簿”。今天直接上干货,告诉你哪些命令用好了封神!喜欢的一键三连,谢谢观众老爷!!...

Linux 命令速查手册:这 30 个高频指令,拯救 90% 的运维小白!

在Linux系统的世界里,命令行是强大的武器。对于运维小白而言,掌握一些高频使用的Linux命令,能极大提升工作效率,轻松应对各种系统管理任务。今天,就为大家奉上精心整理的30个Linu...

linux必学的60个命令(linux必学的20个命令)

以下是Linux必学的20个基础命令:1.cd:切换目录2.ls:列出文件和目录3.mkdir:创建目录4.rm:删除文件或目录5.cp:复制文件或目录6.mv:移动/重命名文件或目录7....

提高工作效率的--Linux常用命令,能够决解95%以上的问题

点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf为什么要学习Linux命令?1、因为Li...

15 个实用 Linux 命令(linux命令用法及举例)

Linux命令行是系统管理员、开发者和技术爱好者的强大工具。掌握实用命令不仅能提高效率,还能解锁Linux系统的无限潜力,本文将深入介绍15个实用Linux命令。ls-列出目录内容l...

Linux 常用命令集合(linux常用命令全集)

系统信息arch显示机器的处理器架构(1)uname-m显示机器的处理器架构(2)uname-r显示正在使用的内核版本dmidecode-q显示硬件系统部件-(SMBIOS/DM...

Linux的常用命令就是记不住,怎么办?

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux常用文件操作命令(linux常用文件操作命令有哪些)

ls命令在Linux维护工作中,经常使用ls这个命令,这是最基本的命令,来写几条常用的ls命令。先来查看一下使用的ls版本#ls--versionls(GNUcoreutils)8.4...

Linux 常用命令(linux常用命令)

日志排查类操作命令查看日志cat/var/log/messages、tail-fxxx.log搜索关键词grep"error"xxx.log多条件过滤`grep-E&#...

简单粗暴收藏版:Linux常用命令大汇总

号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部下午好,我的网工朋友在Linux系统中,命令行界面(CLI)是管理员和开发人员最常用的工具之一。通过命令行,用户可...

「Linux」linux常用基本命令(linux常用基本命令和用法)

Linux中许多常用命令是必须掌握的,这里将我学linux入门时学的一些常用的基本命令分享给大家一下,希望可以帮助你们。总结送免费学习资料(包含视频、技术学习路线图谱、文档等)1、显示日期的指令:d...

Linux的常用命令就是记不住,怎么办?于是推出了这套教程

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux的30个常用命令汇总,运维大神必掌握技能!

以下是Linux系统中最常用的30个命令,精简版覆盖日常操作核心需求,适合快速掌握:一、文件/目录操作1.`ls`-列出目录内容`ls-l`(详细信息)|`ls-a`(显示隐藏文件)...

Linux/Unix 系统中非常常用的命令

Linux/Unix系统中非常常用的命令,它们是进行文件操作、文本处理、权限管理等任务的基础。下面是对这些命令的简要说明:**文件操作类:*****`ls`(list):**列出目录内容,显...