【平头哥RVB2601创意应用开发】基于RISC-V的手势识别云音乐播放器
[复制链接]
由于RISC-V架构是一个开源、免费的架构,这几年确实受到了全球厂商们的关注和使用,目前RISC-V基金会成员已有超过2000家,覆盖了70个国家。像Intel这样的传统X86大厂都做出了违背祖宗的决定加入到RISC-V架构阵营中(收购SiFive),但大概是做一些智能驾驶汽车上使用的芯片。它的优势在于免费、开源,甚至是可以让我们修改它的指令集,但是修改后的芯片就不符合RISC的标准,需要为其自定义名称。
手势识别的应用场景广泛,结合物联网,可以实现在包括智能驾驶中操控汽车、游戏娱乐、AR/VR体验和培训等多种场景下的应用。在这些应用场景中,手势识别技术需要足够稳定和灵敏,才能为用户带来顺畅无阻的体验。此外,手势识别需要足够通用,可适应各种不同的环境和照明条件。在应用方面,与按键相比,手势识别无需眼睛找准按键方向,只需比划相应的手势即可执行相应的操作;与手柄相比,手势的优势在于使用更灵活,而且沉浸感更好,直接上手体验会比手柄按键更自然。
阿里云物联网平台是一个集成了设备管理、数据安全通信和消息订阅等能力的一体化平台。向下支持连接海量设备,采集设备数据上云;向上提供云端API,服务端可通过调用云端API将指令下发至设备端,实现远程控制。利用WIFI通信,接入物联网平台,能实现云下发任务。
本项目的名称为《基于RISC-V的手势识别云音乐播放器》,鉴于我非常喜欢听音乐,做个音乐播放器的小项目便在情理之中。之前只做过C51、STM32相关的音乐播放器,还从未接触过RISC-V架构。 由于平头哥和阿里的关系,阿里推出IOT物联网平台,结合物联网应用,做一款基于CH2601的手势识别云音乐播放器是一件很有实际意义的事情。正好平头哥搞了活动,于是果断申请,过了几周就批准了。
RISC-V正处于发展初期,各种外部模块的驱动程序急需适配。为了减少引脚使用,决定选用PAJ7620U2手势识别模块,而PAJ7620在RVB2601开发板上还没有例程,对应于CH2601芯片的驱动也没有现成的可用,只能自己移植。对于12864显示屏,由于lvgl我也不太会用,于是移植了ssd1306的相关驱动并实现了音乐信息的显示。为了减轻处理器的压力,还自行编写了PHP接口,使用PHP远程获取音乐文件的相关信息,可以分别在云平台和RVB2601的显示屏上显示。
最终,我完成了以下任务:实现了音乐播放器的完整功能;不同手势控制播放器的播放进程、音量大小等基本操作;自行编写了2个API接口用于获得歌曲的长度、码率等信息;实现阿里云平台调用自定义API接口并下发到RVB2601;实现了阿里云平台下发控制不同命令到RVB2601并执行相应操作控制播放进程;移植了PAJ7620系列驱动和SSD1306驱动,使其支持CH2601芯片;阿里IOT云平台的布局(对应逻辑功能全部实现)等。
PAJ7620模块与CH2601芯片之间的连接如图13,VCC与GND分别对应芯片引脚上的VCC和GND,SCL和SDA分别对应芯片引脚上的PA7和PA26。
本项目是以例程现有的网络播放器项目为基础改造而成,增加了手势识别控制,OLED显示歌曲信息,连接阿里云物联网平台和物联网平台下发命令等功能。下面我将逐一说明每一个功能实现的要点和关键代码。
首先说手势识别控制,手势识别模块选用了PAJ7620模块,支持9种手势,通过IIC协议与主机通信。初始化PAJ7620我使用了两个文件,分别是myi2c.c和paj7620.c,myi2c主要是初始IIC协议,使用的方法就是引脚软件模拟IIC,函数具体就不列出,可以参考stm32下的代码,这里主要修改了头文件中的操作定义,
编写完myi2c.c文件,我们的paj7620基本就初始化完成一半了。Paj7620的初始化函数不算太难,首先调用前面编写的IIC初始化函数,之后唤醒paj7620(向其传送一些信号),若唤醒不成功则返回0,如果唤醒成功就继续执行初始化操作,即向PAJ7620寄存器内写入初始化数组。初始化成功后,便会打印出成功字样并返回1。
在这之后,就可以正式编写手势识别代码了,在这里我使用的有返回值的函数,可以根据返回不同的值执行对应的操作,函数的核心是运用前面编写在myi2c.c中的GS_ReadnByte()函数读取寄存器中的值。
最后一步即使调用手势识别函数,我们在sdk自带的函数player.c中的相应部分调用这个函数即可(我是直接将函数写在了对应处)。
在这里,我把播放相关的操作封装到了PlayControl()函数里,若要对播放实例进行控制操作,只需要在参数中增加相应参数即可。
在这里一并附上获取音量函数、获取音乐名函数和获取云平台上传入的URL函数(这个函数是为了在OLED上显示文件名而编写的)。获取音乐名函数就是获取URL中的文件名。
第二说说SSD1306驱动12864屏幕,这个社区有了一篇文章,具体就不细说,也是移植来的(因为本人毕业设计就使用了SSD1306的0.96OLED显示屏),在原有基础上增加了ssd1306驱动OLED显示屏并显示歌曲的歌曲名称、当前时长、总时长、码率、音量大小等等。所以在这里只说说与STM32的不一样的地方。
在这一块主要用了这两个函数,第一个是OLED初始化函数,第二个则是更新屏幕显示函数。
初始化函数由四部分组成,前两部分是初始化引脚功能和通信协议,与STM32的区别主要在语法方面有差异。
做完上述工作后,就真正进入到了OLED的初始化阶段,这一部分的函数编写可以完全参照STM32下的驱动:
关键来了,本设计的所有显示都是调用ssd1306_screen_update()这个函数,写入数据分三步曲,第一步将数据放入数组内,第二步选择写入的行坐标,第三步调用WriteString()函数,写入到OLED缓存中。一屏数据写好后,调用UpdateScreen()函数刷新整屏,这样就能在12864屏幕上显示我们需要的数据了。其中timer2是根据计算得到的当前播放到的秒数,getDutation()函数是获取到的歌曲的秒数,getKbps函数()是获取到的码率。这三个数据都与IOT平台有关,故在此不多加赘述。
第三,IOT平台的调用问题,也就是连接阿里云物联网平台和物联网平台下发命令。关于这个问题,平头哥社区有篇文章,是基于网络播放器项目的,大家可以参考。这个函数就是从WiFi接收到IOT平台的发送请求后需要调用的,请求函数写在了w800_api中。在这个函数里我要求云平台要给出三个数据,分别是播放总长度,码率和播放地址。给出播放地址后马上就转入播放了,故要先获得前两个数据。
由于每次只能获取一个参数,如果不加if判断就会导致传参错误,最终导致死机。经过我的长时间测试,得到一种方法:在机器中预先定义一个布尔型变量用于了解是否需要更新数据,在云平台也定义这个变量。当需要对这个数据更新时,先传输这个布尔型变量使其变成真,再向机器传输第二次数据,将需要更新的数据更新进来,同时将布尔型变量变成假,就巧妙地解决了死机的问题。
接收代码如下框所示,这是一个具体的例子,可以参考一下。
int iot_apply(char *strbuff)
{
//printf("SDSDSD %d\n",SongDuration);
strcpy((char *)buff_accept,strbuff);
str_json = cJSON_Parse((char *)buff_accept); //创建JSON解析对象,返回JSON格式是否正确
if(str_json == NULL)
{
LOGI(TAG"error:%s;\r\n",cJSON_GetErrorPtr());
cJSON_Delete(str_json);//释放内存
return -1;
}
else
{
if(trans_Kbps==true){
trans_Kbps=false;
str_value = cJSON_GetObjectItem(str_json, "Kbps");
Kbps = atoi(str_value->valuestring);
}
if(trans_duration==true){
trans_duration=false;
str_value = cJSON_GetObjectItem(str_json, "SongDuration");
SongDuration = atoi(str_value->valuestring);
//printf("theDuration:%d\n",getDuration);
}
if(trans_url==true){
trans_url=false;
str_value = cJSON_GetObjectItem(str_json, "PlayURL");
//LOGI(TAG,"PlayURL:%s;\r\n",str_value->valuestring);
URLPlay(str_value->valuestring);
}
str_value = cJSON_GetObjectItem(str_json, "VolumeControl");
//LOGI(TAG,"VolumeControl:%d;\r\n",str_value->valueint);
if(str_value->valueint==0){
PlayControl(2);//降低音量
}
if(str_value->valueint==1){
PlayControl(1);//增加音量
}
str_value = cJSON_GetObjectItem(str_json, "MusicPlayControl");
//LOGI(TAG,"MusicPlayControl:%d;\r\n",str_value->valueint);
if(str_value->valueint==0){
//iot_upload(2);
PlayControl(6);//播放
}
if(str_value->valueint==1){
//iot_upload(3);
PlayControl(7);//暂停
}
if(str_value->valueint==4){
//iot_upload(0);
PlayControl(8);//停止
}
str_value = cJSON_GetObjectItem(str_json, "DataRequest");
if(str_value->valueint==0){
trans_duration=true;
}
if(str_value->valueint==1){
trans_url=true;
}
if(str_value->valueint==2){
trans_Kbps=true;
}
cJSON_Delete(str_json);//释放内存
//printf("theDuration:%d\n",SongDuration);
return 1;
}
}
五、作品源码
1.RVB2601源码:
蓝奏云下载:https://wwp.lanzouj.com/iGWIy04jn3ej 密码:7cvk
百度云下载:链接: 提取码: uwac
2.PHP自定义API接口源码:
蓝奏云下载:https://wwp.lanzouj.com/iUGQo04jng4h 密码:36u7
百度云下载: 提取码: f46e
六、视频演示
1.手势识别控制效果展示:
哔哩哔哩:https://www.bilibili.com/video/BV1ir4y1t7Do/
2.阿里云物联网平台控制效果展示:
哔哩哔哩:https://www.bilibili.com/video/BV1ou411r79q/
七、项目总结
1.总结:
关于总结,第一便是基本功不扎实。由于本身是学计算机出身,嵌入式对于我来说是弱项。比如IIC协议的原理,SPI协议的原理等理解的不到位,但在这种情况下依然把PAJ7620的驱动移植到ch2601,让我的学习兴趣变得更加浓厚了。在后续的学习过程中要继续夯实基础,加深对基础性问题的认识,把相应的原理弄透弄扎实。
第二,对于阿里云平台不是很熟练。这个平台在我使用的过程中还是存在一些问题,最大的问题是文档和例子很匮乏,很多东西都要自己摸索,极为浪费时间。其次是平台的一些功能待改进,比如添加按钮的操作最好可以直接复制其他按钮的操作而不是每次都要重新配置,按钮绑定功能误删后即使平台存在撤销恢复功能也不能够恢复等等问题,还有一些细节问题不再赘述。
第三,对于RVB2601本身而言,个人感觉还是非常不错的,性能足够强劲,上手也很容易。唯一不足可能就是现在刚刚起步,资料、例程较为缺乏,希望厂商能够想办法让更多的开发者投入到这一平台中,逐渐构建一个更好更强大的生态环境。
2.帖子分享链接汇总:
- 第一次上手,成功运行hello world!项目记:
https://bbs.eeworld.com.cn/thread-1199598-1-1.html
- 为webplayer播放例程增加串口调节音量:
https://bbs.eeworld.com.cn/thread-1201357-1-1.html
八、其他
邓皓天-基于RISC-V的手势识别云音乐播放器.doc
(2.82 MB, 下载次数: 8)
|