使用树莓派3B通过HDMI播放视频。并且使用串口去控制播放哪个视频。
1. 问题解耦
单片机串口控制树莓派3B播放视频? = 树莓派播放视频 + 单片机串口传参控制树莓派
-
树莓派播放视频:树莓派播放视频,并且能用串口这种简单的通信方式去控制。应该是需要安装一个,只要命令行就可以控制的视频播放器。这里我们选用omxplayer。
-
单片机串口传参控制树莓派:树莓派使用串口通信,解析串口消息帧,解析出视频编号,传入 omxplayer的命令行。这里我们使用 wiringPi库的串口函数,去进行串口消息的解析。解析出视频编号后,调用命令行,控制树莓派播放视频。
2. 串口通信帧协议,视频文件编号
单片机和树莓派的串口通信帧协议,我是这样定义的:
- 每帧5个字节。其中2个字节为帧头,2个字节为帧尾,用来标记消息帧。第3个字节为要播放的视频编号,可以从 0 ~ 255。
(只定义了播放功能,并没有对暂停、结束、快进快退进行定义)
单片机和树莓派的串口通信消息帧协议
帧头1 |
帧头2 |
视频编号 |
帧尾1 |
帧尾2 |
0x3A |
0x3B |
0 ~ 255 |
0x7E |
0x7F |
- 视频事先使用FTP传输,存储于树莓派的内存卡中,视频文件存放目录为 /home/pi/ ,也就是用户目录 ~ 。
- 统一视频文件的命名规则,进行编号:总计四位字符,0000 ~ 0255。例:0001.mp4,0058.mp4,0233.mp4。
例如:如果我们需要控制树莓派播放 0001.mp4 这个视频,只需要串口发送 3A 3B 01 7E 7F(十六进制数据)。
3. 安装播放器omxplayer
4. C代码
我们通过对 串口数据解析代码 的修改,增加 omxplayer的控制代码,实现播放对应编号的 .mp4视频。
serial.c
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdlib.h>
// 最大帧长度
#define MaxFrameLength 10 // 对最大帧长度加以限定,防止接收到过长的帧数据
// 完整的帧头,2个字节
#define Frame_Header1 0x3A // 串口接收消息包的帧头的第1个字节
#define Frame_Header2 0x3B // 串口接收消息包的帧头的第2个字节
// 完整的帧尾,2个字节
#define Frame_Tail1 0x7E // 串口接收消息包的帧尾的第1个字节
#define Frame_Tail2 0x7F // 串口接收消息包的帧尾的第2个字节
int main(void)
{
int hs1;
int snum = 0; // 系统串口接收缓存区的可用字节数
int tnum = 0; // 用户串口接收区的数目
unsigned char chrtmp[100]; // 用户串口接收区,将缓存的数据放入这里处理
int f_h1_flag = 0; // 接收到帧头的第一个字节标志位
int f_h_flag = 0; // 接收到帧头标志位
int f_t1_flag = 0; // 接收到帧尾的第一个字节标志位
wiringPiSetup(); // 使用wiring编码去初始化GPIO序号
//hs1 = serialOpen("/dev/ttyS0", 115200); // 打开 /dev/ttyS0 串口设备,波特率115200
hs1 = serialOpen("/dev/ttyUSB0", 115200); // 打开 /dev/ttyUSB0 串口设备,波特率115200
/*printf("ttyS0 uart test2:\n"); // 终端打印
serialPrintf(hs1, "Hello World!\r\n"); // 串口打印
serialPrintf(hs1, "Enter a paragraph and end with Enter:\r\n");*/
while (1)
{
snum = serialDataAvail(hs1); // 获取串口接收缓存区的可用字节数
if(snum > 0)
{
chrtmp[tnum] = serialGetchar(hs1); // 从接收缓存区读取一个字节
if (f_h_flag == 1) // 有帧头,判断帧尾,接收消息
{
if (f_t1_flag == 1) //有帧头,有帧尾1
{
if (chrtmp[tnum] == Frame_Tail2)
{
/* 用户处理代码 */
/* 将接收到符合帧定义的帧,原路发送回去 */
int i = 0;
for (i = 0; i < (tnum + 1); i++)
{
serialPutchar(hs1, chrtmp[i]); // 通过串口发送字节
}
//解析帧消息,播放对应编号的 .mp4视频
char str1[50], str2[50];
sprintf(str2, "echo \"\nomxplayer -o hdmi /home/pi/%04d.mp4\n\"", chrtmp[2]);
system(str2);
sprintf(str1, "omxplayer -o hdmi /home/pi/%04d.mp4", chrtmp[2]);
system(str1);
/* 处理完用户代码,重新接收计数 */
tnum = 0;
}
else
{
f_t1_flag = 0;
tnum ++;
}
}
else // 有帧头,无帧尾1
{
if (chrtmp[tnum] == Frame_Tail1)
{
f_t1_flag = 1;
tnum ++;
}
else // 接收消息包中间内容
{
tnum ++;
}
}
}
else // 没有接收到帧头
{
if (f_h1_flag == 1) //没有帧头,有帧头1。下一步判断是否为第2个字节
{
if (chrtmp[tnum] == Frame_Header2) // 如果为帧头的第2个字节,接收到帧头标志位标志位置1,tnum自增
{
f_h_flag = 1;
tnum ++;
}
else
{
f_h1_flag = 0;
tnum = 0;
}
}
else //没有帧头,没有有帧头1。下一步判断,是否为帧头的第1个字节
{
if (chrtmp[tnum] == Frame_Header1) // 如果为帧头的第1个字节,标志位置1,tnum自增
{
f_h1_flag = 1;
tnum ++;
}
else // 否则,标志位清0,tnum清0
{
tnum = 0;
}
}
}
// 大于MaxFrameLength个字节的帧不接收
if (tnum > (MaxFrameLength - 1) )
{
tnum = 0;
f_h1_flag = 0;
f_h_flag = 0;
f_t1_flag = 0;
continue;
}
}
}
return 0;
}
5. 运行
- 我在树莓派的 /home/pi/ 目录,使用FTP传输, 存放了两个视频:0001.mp4、0002.mp4。
- 之后再将 serial.c 通过FTP,存放于 demo/serial_hdmi_video_complete/c_demo/ 目录下。
- 编译生成可执行文件:gcc serial.c -o serial -l wiringPi
- 运行:./serial
- 将显示器的 HDMI线连接到 树莓派的HDMI接口上。
- 使用串口助手,115200波特率,十六进制发送 3A 3B 01 7E 7F,播放 /home/pi/0001.mp4。
- 之后再发送 3A 3B 02 7E 7F,播放 /home/pi/0002.mp4。
测试过程:
6. 实际播放效果
7. 相关链接
omxplayer基本使用:树莓派3B omxplayer基本使用
树莓派3B串口通信及解析代码:树莓派3B串口通信及解析代码