1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
4)对正点原子Linux感兴趣的同学可以加群讨论:935446741

第二十五章 语音识别项目

我们知道AI智能音箱已经在我们生活中不少见,也许我们都玩过,智能化非常高,功能强大,与我们平常玩的那种蓝牙音箱,Wifi音箱有很大的区别,AI智能在哪里呢?语音识别技术和云端技术,主要由主控芯片,麦克风阵列,功率放大,codec,触控电路,LED阵列组成。
AI音箱对传统音箱主要有两大块的技术区别,一块是语音信号的前处理,包括回声消除、波速成型、音源定位、降噪、去混响、自动语音电平控制这块是偏硬件的控制。还有一块是智能语音交互,包括语音关键词搜索、本地语音识别、声纹识别、语音合成。
AI智能音箱的芯片方案商:联发科,全志科技,瑞芯微等等,语音识别都有现成的方案商。他们的麦克风阵列方案,有2麦,4麦,6麦,7 + 1麦等等。
写上面这些是让读者了解一下专业AI音箱方案与我们在正点原子Linux开发板想实现语音识别的差别在哪里。我们在正点原子Linux开发板上实现语音识别项目(功能),就不能与专业的AI音箱对比了。硬件资源有限,开发板只有一个麦头(咪头座),没有那些硬件控制消除回声,降噪等等。不过编者在上面调用百度语音API识别语音,识别率还是挺高的。
下面就与大家一起在正点原子Linux IMX6U开发板上实现语音识别功能吧!注意:正点原子MINI I.MX6U开发板没有音频芯片,不支持此实验,只有正点原子I.MX6U ALPHA开发板支持。
本章简介如下:
介绍百度语音技术账号申请,及简单介绍调用流程。
用Qt编写示例程序。流程如下,录制音频后,发送调用百度语音识别API接口,识别并返回结果。支持语音控制正点原子I.MX6U开发板上的LED控制,其他设备可以自行拓展。

25.1 语音识别产品申请帐号

语音识别技术产品,有讯飞,百度等厂家,我们可以购买或者免费试用他们的产品。可以直接到他们的官网上查看,有使用技术文档。下面我们以百度语音识别技术产品为例子。可以在浏览器输入搜索“百度语音识别”,就可以找到百度AI开放平台。

点击进去就可以看到他的技术文档链接位置。如下图。

或者直接打开https://ai.baidu.com/ai-doc/SPEECH/Ek39uxgre就可以跳转到百度AI开放平台》帮助文档》语音技术页面。如下图。

请仔细阅读百度语音技术的文档,里面写的非常详细,还有例子下载参考。
编者阅读总结,想要使用百度语音识别接口,需要根据上面图中的新手指南注册百度帐号,领取免费额度及创建中文普通话应用(创建前先领取免费额度(180天免费额度,可调用约5万次左右,详细请看免费额度说明))。记住自己的密钥。请自行完成及创建百度帐号,按照百度帮助文档里的步骤,领取免费额度及创建中文普通话应用,获取密钥!程序里需要用到自己的密钥。编者提供的密钥是百度语音识别例程里的,如果开发次数超了可能就不能使用了。程序中只需要API Key与Secret Key。注意获取Access Token时有效期为30天,到期后需要在程序里重新获取新的token。

更多参考请查看百度AI接入指南。
注意,帮助文档里提及SDK包,有LinuxC++SDK包支持,但是目前仅支持 X64(x86-64) CPU架构的 Linux 操作系统。LinuxSDK 仅支持在线语音识别,固定长语音模式。简单的说就是还不支持ARM架构的SDK包。

25.2 百度语音识别流程及示例简介

在百度AI帮助文档里可以看见如下重要信息。

请认真阅读调用流程,了解操作过程,对下面理解编者编写Qt调用百度语音API的例子会有一定的帮助。
总结:调用流程需要仔细阅读,百度提供了示例Demo代码,可以看到里面支持很多种编程语言编写的API请求相关示例demo代码。没有直接C++相关的代码。C语言是C++语言的子集,我们可以直接参考C语言编写的例子(请自行查阅及参考百度提供的C语言编写的API请求相关示例demo代码)来编写Qt调用语音识别API。(备注:其他语言编写的例子不在我们教程范围。)识别的音频格式支持如上,我们可以知道一些重要的信息是支持采样率16000、8000的固定值,16bit深的单声道,音频长度最长60秒。格式支持wav,恰好正点原子Linux I.MX6U开发板系统支持wav格式播放及录制(详细请看【正点原子】I.MX6U用户快速体验V1.x.pdf测试音频部分)。
备注:由于百度语音识别的API例子放在github(开源网站),国外网站的原因,可能打开失败,请多次尝试,如果一直无法访问,那么我们直接往下看使用编者编写Qt的示例吧。不能访问的话,编者也没办法的。

25.3 百度短语音识别API接口

源码路径为4/02_asr_demo/asr/asr.h,内容如下。asr是语音识别功能demo,(asr译作自动语音识别技术即automatic speech recognition)

    /******************************************************************Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.* @projectName   asr* @brief         asr.h* @author        Deng Zhimao* @email         1252699831@qq.com* @net            www.openedv.com* @date           2021-06-03*******************************************************************/
1   #ifndef ASR_H
2   #define ASR_H
3
4   #include <QWidget>
5
6   #include <QNetworkAccessManager>
7   #include <QNetworkReply>
8
9   #include <QJsonDocument>
10  #include <QJsonParseError>
11  #include <QJsonObject>
12  #include <QJsonArray>
13  #include <QHostInfo>
14
15  #include <QFile>
16
17  class Asr : public QWidget
18  {19      Q_OBJECT
20
21  public:
22      Asr(QWidget *parent = nullptr);
23      ~Asr();
24
25      /* 请求网络 */
26      void requestNetwork(QString, QByteArray);
27
28      /* 获取识别结果 */
29      void getTheResult(QString fileName);
30
31  private:
32      /* 存储获取tokenUrl地址 */
33      QString tokenUrl;
34
35      /* 存储serverapi地址 */
36      QString serverApiUrl;
37
38      /* 最终需要访问token的地址 */
39      QString accessToken;
40
41      /* 获取token的接口*/
42      const QString token_org = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2&";
43
44      /* 填写网页上申请的appkey 如 g_api_key="g8eBUMSokVB1BHGmgxxxxxx" */
45      const QString api_key = "kVcnfD9iW2XVZSMaLMrtLYIz";
46
47      /* 填写网页上申请的APP SECRET 如 $secretKey="94dc99566550d87f8fa8ece112xxxxx" */
48      const QString secret_key = "O9o1O213UgG5LFn0bDGNtoRN3VWl2du6";
49
50      /* 百度服务器API接口,发送语音可返回识别结果 */
51      const QString server_api = "http://vop.baidu.com/server_api?dev_pid=1537&cuid=%1&token=%2";
52
53      /* 网络管理 */
54      QNetworkAccessManager *networkAccessManager;
55
56      QString getJsonValue(QByteArray ba, QString key);
57
58      QFile file;
59
60  private slots:
61
62      /* 准备读取响应返回来的数据 */
63      void readyReadData();
64
65      /* 响应完成处理 */
66      void replyFinished();
67
68  signals:
69      void asrReadyData(QString);
70
71  };
72  #endif // ASR_H
第45行,请填写读者自己在网页上申请的API Key。以防万一示例中的API Key过期不可用!
第47行,请填写读者在网页上申请的Secret Key。以防万一示例中的Secret Key过期不可用!
其他地址由来是见百度给出的Demo示例,及百度的帮助文档。这里就不详细说了。原理与上一章原子云API接口相似。不过百度语音识别需要通过自己的帐号,指定地址获取访问的Token源地址,然后将得到的Access Token地址与语音识别服务器地址拼接,发送语音到服务器,就可以返回识别的结果了。详细请参考源码4/02_asr_demo/asr/asr.cpp。

25.4 录制wav音频

在12.5小节,已经介绍过开发板如何录制音频文件了,详细请看12.5小节,就不详细介绍了,注意需要修改的地方如下。因为百度语音识别支持采样率16000、8000的固定值,16bit深的单声道,音频长度最长60秒。格式支持wav,pcm等格式。我们需要修改录制音频格式为wav格式,通道为单声道,采样率为16000,源码如下,已用红色字体标出。录制的音频文件保存为本地16k.wav文件。
源码路径为4/02_asr_demo/audiorecorder/audiorecorder.cpp。

    /******************************************************************Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.* @projectName   audiorecorder* @brief         audiorecorder.cpp* @author        Deng Zhimao* @email         1252699831@qq.com* @net            www.openedv.com* @date           2021-06-04*******************************************************************/
1   #include "audiorecorder.h"
2   #include <QDebug>
3   #include <QUrl>
4   #include <QDateTime>
5   #include <QDir>
6   #include <QCoreApplication>
7
8   static qreal getPeakValue(const QAudioFormat &format);
9   static QVector<qreal> getBufferLevels(const QAudioBuffer &buffer);
10
11  template <class T>
12  static QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels);
13
14  AudioRecorder::AudioRecorder(QWidget *parent)
15  {16      Q_UNUSED(parent);
17
18      /* 录制音频的类 */
19      m_audioRecorder = new QAudioRecorder(this);
20
21      /* 用于探测缓冲区的数据 */
22      m_probe = new QAudioProbe(this);
23
24      /* 信号槽连接,更新录音level显示 */
25      connect(m_probe, &QAudioProbe::audioBufferProbed,
26              this, &AudioRecorder::processBuffer);
27
28      /* 设置探测的对象 */
29      m_probe->setSource(m_audioRecorder);
30
31      /* 扫描本地声卡设备 */
32      devicesVar.append(QVariant(QString()));
33      for (auto &device: m_audioRecorder->audioInputs()) {34          devicesVar.append(QVariant(device));
35          //qDebug()<<"本地声卡设备:"<<device<<endl;
36      }
37
38      /* 音频编码 */
39      codecsVar.append(QVariant(QString()));
40      for (auto &codecName: m_audioRecorder->supportedAudioCodecs()) {41          codecsVar.append(QVariant(codecName));
42          //qDebug()<<"音频编码:"<<codecName<<endl;
43      }
44
45      /* 容器/支持的格式 */
46      containersVar.append(QVariant(QString()));
47      for (auto &containerName: m_audioRecorder->supportedContainers()) {48          containersVar.append(QVariant(containerName));
49          //qDebug()<<"支持的格式:"<<containerName<<endl;
50      }
51
52      /* 采样率 */
53      sampleRateVar.append(QVariant(0));
54      /* 百度语音识别只支持8000、 16000采样率 */
55      sampleRateVar.append(QVariant(8000));
56      sampleRateVar.append(QVariant(16000));
57      for (int sampleRate: m_audioRecorder->supportedAudioSampleRates()) {58          sampleRateVar.append(QVariant(sampleRate));
59          //qDebug()<<"采样率:"<<sampleRate<<endl;
60      }
61
62
63      /* 通道 */
64      channelsVar.append(QVariant(-1));
65      channelsVar.append(QVariant(1));
66      channelsVar.append(QVariant(2));
67      channelsVar.append(QVariant(4));
68
69      /* 质量 */
70      qualityVar.append(QVariant(int(QMultimedia::LowQuality)));
71      qualityVar.append(QVariant(int(QMultimedia::NormalQuality)));
72      qualityVar.append(QVariant(int(QMultimedia::HighQuality)));
73
74      /* 比特率 */
75      bitratesVar.append(QVariant(0));
76      bitratesVar.append(QVariant(32000));
77      bitratesVar.append(QVariant(64000));
78      bitratesVar.append(QVariant(96000));
79      bitratesVar.append(QVariant(128000));
80
81      /* 录音类信号槽连接 */
82      connect(m_audioRecorder, &QAudioRecorder::durationChanged,
83              this, &AudioRecorder::updateProgress);
84  }
85
86  AudioRecorder::~AudioRecorder()
87  {88  }
89
90
91  void AudioRecorder::startRecorder()
92  {93      /* 备注:录音需要设置成16000 采样率和通道数为1,
94       * 保存为wav文件需要设置成audio/x-wav(container文件格式) */
95
96      /* 如果录音已经停止,则开始录音 */
97      if (m_audioRecorder->state() == QMediaRecorder::StoppedState) {98          /* 设置默认的录音设备 */
99          m_audioRecorder->setAudioInput(devicesVar.at(0).toString());
100
101         /* 下面的是录音设置 */
102         QAudioEncoderSettings settings;
103         settings.setCodec(codecsVar.at(0).toString());
104         settings.setSampleRate(sampleRateVar[2].toInt());
105         settings.setBitRate(bitratesVar[0].toInt());
106         settings.setChannelCount(channelsVar[1].toInt());
107         settings.setQuality(QMultimedia::EncodingQuality(
108                                 qualityVar[0].toInt()));
109
110         /* 以恒定的质量录制,可选恒定的比特率 */
111         settings.setEncodingMode(QMultimedia::ConstantQualityEncoding);
112
113         /* I.MX6ULL第20个支持的格式为 audio/x-wav */
114         QString container = containersVar.at(20).toString();
115
116         /* 使用配置 */
117         m_audioRecorder->setEncodingSettings(settings,
118                                              QVideoEncoderSettings(),
119                                              container);
120         /* 录音保存为16k.wav文件 */
121         m_audioRecorder->setOutputLocation(QUrl::fromLocalFile(tr("./16k.wav")));
122
123         /* 开始录音 */
124         m_audioRecorder->record();
125     }
126 }
127
128 void AudioRecorder::stopRecorder()
129 {130     /* 停止录音 */
131     m_audioRecorder->stop();
132 }
133
134
135 void AudioRecorder::updateProgress(qint64 duration)
136 {137     Q_UNUSED(duration);
138
139     if (m_audioRecorder->error()
140             != QMediaRecorder::NoError)
141         return;
142
143     /* 打印录制时长 */
144     //qDebug()<<duration / 1000<<endl;
145 }
146
147
148 void AudioRecorder::clearAudioLevels()
149 {150     //...
151 }
152
153 // This function returns the maximum possible sample value for a given audio format
154 qreal getPeakValue(const QAudioFormat& format)
155 {156     // Note: Only the most common sample formats are supported
157     if (!format.isValid())
158         return qreal(0);
159
160     if (format.codec() != "audio/pcm")
161         return qreal(0);
162
163     switch (format.sampleType()) {164     case QAudioFormat::Unknown:
165         break;
166     case QAudioFormat::Float:
167         if (format.sampleSize() != 32) // other sample formats are not supported
168             return qreal(0);
169         return qreal(1.00003);
170     case QAudioFormat::SignedInt:
171         if (format.sampleSize() == 32)
172             return qreal(INT_MAX);
173         if (format.sampleSize() == 16)
174             return qreal(SHRT_MAX);
175         if (format.sampleSize() == 8)
176             return qreal(CHAR_MAX);
177         break;
178     case QAudioFormat::UnSignedInt:
179         if (format.sampleSize() == 32)
180             return qreal(UINT_MAX);
181         if (format.sampleSize() == 16)
182             return qreal(USHRT_MAX);
183         if (format.sampleSize() == 8)
184             return qreal(UCHAR_MAX);
185         break;
186     }
187
188     return qreal(0);
189 }
190
191 // returns the audio level for each channel
192 QVector<qreal> getBufferLevels(const QAudioBuffer& buffer)
193 {194     QVector<qreal> values;
195
196     if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian)
197         return values;
198
199     if (buffer.format().codec() != "audio/pcm")
200         return values;
201
202     int channelCount = buffer.format().channelCount();
203     values.fill(0, channelCount);
204     qreal peak_value = getPeakValue(buffer.format());
205     if (qFuzzyCompare(peak_value, qreal(0)))
206         return values;
207
208     switch (buffer.format().sampleType()) {209     case QAudioFormat::Unknown:
210     case QAudioFormat::UnSignedInt:
211         if (buffer.format().sampleSize() == 32)
212             values = getBufferLevels(buffer.constData<quint32>(), buffer.frameCount(), channelCount);
213         if (buffer.format().sampleSize() == 16)
214             values = getBufferLevels(buffer.constData<quint16>(), buffer.frameCount(), channelCount);
215         if (buffer.format().sampleSize() == 8)
216             values = getBufferLevels(buffer.constData<quint8>(), buffer.frameCount(), channelCount);
217         for (int i = 0; i < values.size(); ++i)
218             values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2);
219         break;
220     case QAudioFormat::Float:
221         if (buffer.format().sampleSize() == 32) {222             values = getBufferLevels(buffer.constData<float>(), buffer.frameCount(), channelCount);
223             for (int i = 0; i < values.size(); ++i)
224                 values[i] /= peak_value;
225         }
226         break;
227     case QAudioFormat::SignedInt:
228         if (buffer.format().sampleSize() == 32)
229             values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount);
230         if (buffer.format().sampleSize() == 16)
231             values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount);
232         if (buffer.format().sampleSize() == 8)
233             values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount);
234         for (int i = 0; i < values.size(); ++i)
235             values[i] /= peak_value;
236         break;
237     }
238
239     return values;
240 }
241
242 template <class T>
243 QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels)
244 {245     QVector<qreal> max_values;
246     max_values.fill(0, channels);
247
248     for (int i = 0; i < frames; ++i) {249         for (int j = 0; j < channels; ++j) {250             qreal value = qAbs(qreal(buffer[i * channels + j]));
251             if (value > max_values.at(j))
252                 max_values.replace(j, value);
253         }
254     }
255
256     return max_values;
257 }
258
259 void AudioRecorder::processBuffer(const QAudioBuffer& buffer)
260 {261     /* 根据通道数目需要显示count个level */
262     int count = buffer.format().channelCount();
263     /* 打印通道数 */
264     Q_UNUSED(count);
265     // qDebug()<<"通道数"<<count<<endl;
266
267     /* 设置level的值 */
268     QVector<qreal> levels = getBufferLevels(buffer);
269     for (int i = 0; i < levels.count(); ++i) {270         /* 打印音量等级 */
271         // qDebug()<<"音量等级"<<levels.at(i)<<endl;
272     }
273 }
4/02_asr_demo/audiorecorder/audiorecorder.cpp主要提供了一个startRecorder()和stopRecorder()的接口,录音保存的文件为可执行程序当前路径下的16k.wav文件。startRecorder()和stopRecorder()分别是开始录音和停止录音。
第54~56行,增加8000和16000的支持项。
第104行,设置为下标为2的项,也就是16000采样率。
第106行,设置通道数下标为1的项,也就是单通道。
第114行,设置文件容器/格式,为audio/x-wav格式(项的下标为20)。设置此格式会保存wav后缀的文件。

25.5 语音界面UI开发

项目路径为4/02_asr_demo/02_asr_demo/02_asr_demo.pro,先看项目界面。项目界面如下,界面简洁大气,界面中间用了一个立体的素材,点击后可以旋转,给人一种智能化的感觉,点击时还会有音效提示,文本提示“请点击,开始说话…”,点击后,提示“正在听您说话,请继续…”,录制8s左右的音频,等待返回识别结果即可。编写设计完成的效果不错!请自行查阅源码,掌握了本教程前面第七章的内容,就可以理解这个界面是如何设计的。

25.6 语音识别项目综合测试

打开4/02_asr_demo/02_asr_demo/02_asr_demo.pro项目,此项目为语音识别UI界面。
打开项目如下图。

项目文件夹下内容解释:
02_asr_demo项目下:

asr文件夹为语音识别的应用程序,主要用来与将录制的音频发送到百度云语音识别服务器上,然后返回识别结果。
aduiorecorder文件夹为录制wav音频的文件夹。主要是用来录制wav音频。
led文件夹为I.MX6U开发板控制LED的接口程序。
Headers文件夹为界面设计的头文件。
Sources文件夹为界面设计的源文件。

25.6.1 Ubuntu上运行

Ubuntu运行后界面如下,注意,Ubuntu需要联网!Ubuntu上理论上是能录制音频识别返回结果的,但是教程主要写正点原子I.MX6U开发板上的语音识别项目。限于编者手上没有可用电脑麦克风,估计读者也没有,电脑配置麦克风输入后可以自行测试。运行之后可以看到下面的界面。Windows不作讲解!请到下面小节使用正点原子I.MX6U ALPHA开发板运行体验识别效果!

25.6.2 ALPHA开发板上运行

本例适用于正点原子I.MX6U ALPHA开发板!请使用正点原子I.MX6U的出厂系统进行测试!
请使用正点原子的I.MX6U的出厂时的系统测试!
请使用正点原子的I.MX6U的出厂时的系统测试!
请使用正点原子的I.MX6U的出厂时的系统测试!
重要的事情是说三遍!
开始录音前,需要根据正点原子I.MX6U用户快速体验手册,第3.15小节进行测试板子的录音功能。确保能正常录音,再交叉编译此Qt应用程序到开发板上运行。如何交叉编译Qt应用程序到开发板,请看【正点原子】I.MX6U 出厂系统Qt交叉编译环境搭建V1.x版本。
在正点原子I.MX6U开发板上运行此录音程序,需要先配置是麦克风(板子上的麦头)。
麦头录音,则在板子上运行开启麦头录音的脚本。

/home/root/shell/audio/mic_in_config.sh
交叉编译到开发板上运行效果如下。下面的图都是开发板上的截图。
程序初始化时。(注意开发板先插上网线联网!确保能上网!)

点击中间的图标后,注意,请在点击1.5~2s后再说话,点击时有音效提醒,避免把音效录进去。整个录音过程是8s左右。

识别返回结果的过程很快,识别率也挺高,如下图,编者说了一句“正点原子”,语音识别返回“正点原子”的结果。注意,识别是中文标准普通话。请尽量说一些日常话语,避免说生僻语句,特殊的方言等。识别常见问题请查看百度AI开发平台的帮助文档。

再点击,再次进行语音识别,话语中,包含“开灯”,那么即可点亮板子上的LED。点亮后,再次进行语音识别,话语中包含“关灯”,即可熄灭板子上的LED。
“开灯”识别结果。

“关灯”识别结果。

本示例仅供学习参考使用,如需要用到开发上,请购买百度或其他开放平台的语音识别产品。

【正点原子Linux连载】第二十五章 语音识别项目 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2相关推荐

  1. 【正点原子Linux连载】第二十二章 AP3216C 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 3)全套实验源码+手册+视频下载地址: ...

  2. 【正点原子Linux连载】 第十九章 CAN Bus 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 3)全套实验源码+手册+视频下载地址: ...

  3. 【正点原子Linux连载】第十四章 Qt控制LED 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  4. 【正点原子Linux连载】第十一章 网络编程 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  5. 【正点原子Linux连载】第十八章 Camera 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 3)全套实验源码+手册+视频下载地址: ...

  6. 【正点原子Linux连载】第六章 Qt Creator的使用技巧 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  7. 【正点原子Linux连载】第五章 文件属性与目录-摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.1

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  8. 【正点原子Linux连载】第五章 RKMedia编译和使用 摘自【正点原子】ATK-DLRV1126系统开发手册

    第五章 RKMedia编译和使用 5.1 RKMedia编译 Rkmedia是RK官方封装一层简易的API,把RGA.MPP.RKNN等等这些接口封装成高级的接口.在SDK官方的源码目录下,运行以下命 ...

  9. 【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  10. 25 linux ndk 头文件_正点原子Linux第二十五章RTC实时时钟实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第二十五章RTC实时时钟实验 实时时钟是很常用的一个外设 ...

最新文章

  1. 微软的python开发工具_面向 Python 开发人员的 Azure 工具
  2. nyist -- 组队赛(一)
  3. 阿里云服务器如何安装memcached
  4. 韦东山衔接班——4.4_构建根文件系统之构建根文件系统
  5. 关于区块链,程序员需要了解什么
  6. 继苹果亚马逊之后 微软加入1万亿美元市值俱乐部
  7. java 银行管理系统怎么储存账户信息_银行管理系统 实现用户注册 登录 存、取款 交易记录查询和修改用户信息等功能...
  8. C#连接Oracle中文乱码问题解决方法
  9. 公开封尘已久的即时通讯源码(转)
  10. laravel ajax分页实例,Laravel 的分页功能 - Laravel 5.6 中文文档
  11. 为什么登录赛尔号显示服务器未开启,赛尔号之勇者无敌无法打开怎么办 赛尔号之勇者无敌登录不了解决方案...
  12. CentOS7.6 部署 Snipe-it 资产管理系统
  13. vue动态设置背景图片
  14. OSI参考模型——数据链路层详解
  15. 一个菜鸟的Geant4入门之路:alpha粒子轰击金箔的例子
  16. [Android]天气App 1
  17. 工业革命的秋之涟漪(三):飞桨,划行在智能经济之海
  18. Nhibernate+MVC开发小结
  19. 〖Python 数据库开发实战 - MySQL篇㉙〗- MySQL 字符函数
  20. 软件体验测试方案,互联网项目用户体验测试报告模板

热门文章

  1. linux 内核函数 filp_open、filp_read、IS_ERR、ERR_PTR、PTR_ERR 简介
  2. Photoshop从入门到放弃
  3. wireshark提示未启动npf服务The NPF driver isn’t running You may have trouble capturing or listing interfaces
  4. 人生,原来是个笑话?
  5. 移动硬盘 无法访问 解决方法
  6. 数据分析报告怎么写?
  7. 邻接表——最简单易懂的写法——向非我非非我大佬低头
  8. 钉钉企业应用网关接入(保姆级教程)
  9. 可能是最漂亮的Spring事务管理详解
  10. 信号与系统 频域分析