1.简介
此文章并不是教程,只能当作笔者的学习分享,只会做一些简单的介绍,其他的各位结合着代码和运行现象自己分析吧,相信通过函数名和注释,基本上是不难看懂代码的,其中涉及到的一些技术栈,也请各位学习到的时候多查阅资料。
本篇的内容为嵌入式Linux应用层的一个综合性比较强的项目,结尾会将源码放在网盘中开源出来,笔者能力有限,只是简单的把功能实现了,代码开源供大家一起交流学习,有什么好的建议,请各位一定不吝赐教!!!
1.1功能介绍
项目包括了四个app:
1.云平台的调试窗口,用于查看订阅主题所下发的数据,另一个为输入Json格式的数据来控制STM32单片机上的外设。
2.智能家居的界面,有4个图片按钮用于控制STM32板子上的LED灯、门(舵机)、蜂鸣器,量计分别为温度、湿度和亮度的值,同样是STM32获取发布到云平台的。
3.通过一个摄像头模块做的一个相机功能,可以拍照、录像,以及查看拍摄的照片,和播放录制视频的回放。
4.简易的音乐播放器:能够切换歌曲,以及暂停播放音乐。
1.2技术栈介绍
虽然项目简单,但是所涉及到的技术栈还是比较杂,我简单在此列出:
1.LVGL库用于绘制UI。
2.MQTT协议,连接阿里云平台与STM32通讯。
3.alsa库用于音频处理。
4.LED、BEEP
5.V4L2 摄像头应用编程
1.3演示视频
【开源】Linux应用综合项目|云平台调试工具+智能家居+相机+音乐播放器_哔哩哔哩_bilibili
1.4硬件介绍
硬件使用的是正点原子的阿尔法开发板,芯片是IMX6U,类似开发板应该都可以运行。
2.软件设计
2.1.asla音频应用编程
此文件初始化了声卡和混音器,创建了一个线程来播放我们所选的音乐,在切换和暂停歌曲时用到了互斥量来保护所要访问的资源,以防切换时出现错误。总共包括了4首歌曲,可以通过左右两个按键来切换上一首和下一首歌曲,可以调节音量和暂停开始播放。
#include "ds_music.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <alsa/asoundlib.h>
/************************************
宏定义
************************************/
#define PCM_PLAYBACK_DEV "hw:0,0"
#define MIXER_DEV "hw:0"
/************************************
WAV音频文件解析相关数据结构申明
************************************/
typedef struct WAV_RIFF
{
char ChunkID[4]; /* "RIFF" */
u_int32_t ChunkSize; /* 从下一个地址开始到文件末尾的总字节数 */
char Format[4]; /* "WAVE" */
} __attribute__((packed)) RIFF_t;
typedef struct WAV_FMT
{
char Subchunk1ID[4]; /* "fmt " */
u_int32_t Subchunk1Size; /* 16 for PCM */
u_int16_t AudioFormat; /* PCM = 1*/
u_int16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */
u_int32_t SampleRate; /* 8000, 44100, etc. */
u_int32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
u_int16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */
u_int16_t BitsPerSample; /* 8bits, 16bits, etc. */
} __attribute__((packed)) FMT_t;
static FMT_t wav_fmt;
typedef struct WAV_DATA
{
char Subchunk2ID[4]; /* "data" */
u_int32_t Subchunk2Size; /* data size */
} __attribute__((packed)) DATA_t;
/************************************
static静态全局变量定义
************************************/
static snd_pcm_t *pcm = NULL; // pcm句柄
static snd_mixer_t *mixer = NULL; // 混音器句柄
static snd_mixer_elem_t *playback_vol_elem = NULL; // 播放<音量控制>元素
static unsigned int buf_bytes; // 应用程序缓冲区的大小(字节为单位)
static void *buf = NULL; // 指向应用程序缓冲区的指针
static int fd = -1; // 指向WAV音频文件的文件描述符
static snd_pcm_uframes_t period_size = 1024; // 周期大小(单位: 帧)
static unsigned int periods = 16; // 周期数(设备驱动层buffer的大小)
static struct termios old_cfg; // 用于保存终端当前的配置参数
static pthread_t g_play_music_thread = NULL;
static pthread_t g_mixer_music_thread = NULL;
bool is_playing = false;
int music_select = 1;
char *music_name, *music_singer;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t volume_mutex = PTHREAD_MUTEX_INITIALIZER;
static int snd_pcm_init(void)
{
snd_pcm_hw_params_t *hwparams = NULL;
int ret;
/* 打开PCM设备 */
ret = snd_pcm_open(&pcm, PCM_PLAYBACK_DEV, SND_PCM_STREAM_PLAYBACK, 0);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_open error: %s: %s\n",
PCM_PLAYBACK_DEV, snd_strerror(ret));
return -1;
}
/* 实例化hwparams对象 */
snd_pcm_hw_params_malloc(&hwparams);
/* 获取PCM设备当前硬件配置,对hwparams进行初始化 */
ret = snd_pcm_hw_params_any(pcm, hwparams);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_any error: %s\n", snd_strerror(ret));
goto err2;
}
/**************
设置参数
***************/
/* 设置访问类型: 交错模式 */
ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (0 > ret)
{
fprintf(stderr, "snd_pcm_hw_params_set_access error: %s\n", snd_strerror(ret));
goto err2;
}
/* 设置数据格式: 有符号16位、小端模式 */
ret = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
if (0 > ret)
{
fprintf(stderr, "