STM32MP157-Linux音频应用编程-简易语音助手
文章目录
- 前言
- STM32MP157简易语音助手
- alsa-lib简介:
- 移植alsa-lib库:
- libcurl库简介:
- 移植libcurl库:
- API调用
- 修改asrmain.c文件
- 修改token.c文件
- 录音
- 文件IO
- 打开音频文件
- 硬件控制
- sysfs文件系统
- 数据解析和控制
- 多线程
- 主循环
- 实现效果及注意事项
- 实现效果
- 注意事项
- 源代码(转载请注明出处)
前言
本篇分享:
Linux应用编程之音频编程,使用户用户可以使用语音控制开发板上的LED灯和蜂鸣器模块。
环境介绍:
系统:Linux
硬件:正点原子STM32MP157开发板
声卡:开发板自带
STM32MP157简易语音助手
实现目标 :用户可以使用语音控制开发板上的LED灯和蜂鸣器模块。
知识点 : C语言、文件IO、alsa-lib 库的使用、libcurl库、API调用、字符串解析、多线程。
在上一篇STM32MP157语音识别项目中,由于之前使用的交叉编译器无法正常编译使用了libcurl库的程序,导致不得不使用execl函数调用CURL指令实现(可能是由于交叉编译器的c库版本和libcurl库的c库版本不同导致)。这样的程序不够灵活(依赖操作系统提供的指令和参数格式)且性能低(调用指令时需要花费额外的系统开销和时间,包括切换上下文、启动子进程、进行系统调用等操作,而使用c库函数则避免了这些额外的开销 )。所以,这次继续沿用Linux语音识别项目中用到的libcurl库来实现。
alsa-lib简介:
alsa-lib 是一套 Linux 应用层的 C 语言函数库,为音频应用程序开发提供了一套统一、标准的接口,应用程序只需调用这一套 API 即可完成对底层声卡设备的操控,譬如播放与录音。
用户空间的 alsa-lib 对应用程序提供了统一的API 接口,这样可以隐藏驱动层的实现细节,简化了应用 程序的实现难度、无需应用程序开发人员直接去读写音频设备节点。所以,主要就是学习 alsa-lib 库函数的使用、如何基于 alsa-lib 库函数开发音频应用程序。
alsa-lib官方说明文档:https://www.alsa-project.org/alsa-doc/alsa-lib/
移植alsa-lib库:
正点STM32MP157开发板出厂已移植(非广告!),需要请参考其他教程。
要在嵌入式linux系统上运行使用alsa-lib库的程序,需要移植alsa-lib库,可以参考网上移植alsa-lib库的方法,或自行下载alsa-lib资源包,自行编译移植。
开源ALSA架构的官网地址:https://www.alsa-project.org/wiki/Main_Page
libcurl库简介:
libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传,代理,cookies,和用户认证。
官网地址:http://curl.haxx.se/
移植libcurl库:
正点STM32MP157开发板出厂已移植(非广告!),需要请参考其他教程。
注意:curl
指令和libcurl
是两个不同的东西,虽然它们都用于处理HTTP请求,但是有以下区别:
curl
指令是一个命令行工具,而libcurl
是一个C语言的库,可以通过函数调用使用。curl
指令可以直接在终端中运行,而libcurl
需要在编程时使用。curl
指令的功能相对简单,主要用于从终端中发送HTTP请求并获取响应,而libcurl
功能更为强大,可以通过编程实现更多复杂的HTTP请求和响应处理操作。curl
指令可以在不同的操作系统和终端上运行,而libcurl
需要在特定的平台上进行编译和部署。
总之,curl
指令是一个简单、方便的工具,可以帮助开发人员快速进行HTTP请求和响应测试。而libcurl
是一个功能更为强大的库,适合于在编程时进行HTTP请求和响应处理,但需要进行编译和部署。
API调用
该程序使用的是百度语音识别API
注册后领取免费额度及创建中文普通话应用(创建前先领取免费额度(180 天免费额度,可调用约 5 万次左右) )
创建好应用后,可以得到API key和Secret Key(填写到程序中的相应位置)
调用API相关说明,Demo代码中有多种语言的调用示例可以参考,使用c语言的话也可以直接在本程序上面再次更改:
API相关c文件中 需要修改的有asrmain.c、token.c和相应的头文件:
修改asrmain.c文件
asrmain.c的fill_config函数中(该函数我已修改,原本无file参数,根据实际情况使用),需要修改的有:音频文件格式,API Key以及Secret Key:
RETURN_CODE fill_config(struct asr_config *config,char *file) {// 填写网页上申请的appkey 如 g_api_key="g8eBUMSokVB1BHGmgxxxxxx"char api_key[] = "填写网页上申请的API key";// 填写网页上申请的APP SECRET 如 $secretKey="94dc99566550d87f8fa8ece112xxxxx"char secret_key[] = "填写网页上申请的Secret Key";// 需要识别的文件char *filename = NULL;filename = file;// 文件后缀仅支持 pcm/wav/amr 格式,极速版额外支持m4a 格式char format[] = "pcm";char *url = "http://vop.baidu.com/server_api"; // 可改为https// 1537 表示识别普通话,使用输入法模型。其它语种参见文档int dev_pid = 1537;char *scope = "audio_voice_assistant_get"; // # 有此scope表示有asr能力,没有请在网页里勾选,非常旧的应用可能没有…………
结合音频录制的程序使用的话,还需要删除示例中的main函数,run函数中的相关初始化以及API调用函数需要根据实际情况重新调整调用位置。本项目总体按照:获取token(在程序开始时获取一次即可,根据官网可知获取的token有效期为30天,重新获取token则之前获取的token失效)->调用API->得到返回结果->解析结果->对硬件控制。
asrmain.c的run_asr函数中(该函数我已修改,原本无result_voice参数,根据实际情况使用),需要修改的有:禁用SSL证书验证(在文件中查到下面这个函数名即可找到需要修改的位置)和 延长连接超时时间(源代码中设定时间为5s,在开发板上连接时间更长,需要改为10s,可能是由于硬件性能较弱或者网络环境不稳定等原因导致 )。
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)
是一个CURL库的选项设置,用于控制CURL库对SSL证书的验证行为。默认情况下,CURL库会验证SSL证书的有效性,以确保请求的安全性。如果SSL证书验证失败,CURL库将阻止请求的进一步处理并返回一个错误。
将CURLOPT_SSL_VERIFYPEER
选项设置为0将禁用SSL证书验证,从而允许不受信任的证书通过。这个设置通常用于调试和测试目的,不建议在生产环境中使用,因为它会降低请求的安全性。如果需要在生产环境中使用不受信任的证书,建议使用自签名证书或受信任的CA签名证书,并通过其他手段验证证书的有效性,而不是禁用SSL证书验证。
修改token.c文件
和上面部分相同,禁用SSL证书验证。
录音
查看Linux语音识别中的录音内容
文件IO
我们需要将录制的音频文件保存到本地,就需要用到文件IO相关知识,打开音频文件以及向音频文件写数据。
打开音频文件
函数:
函数原型:
FILE *fopen(const char *filename, const char *mode)参数:
filename -- 字符串,表示要打开的文件名称。
mode -- 字符串,表示文件的访问模式。作用:
以指定的方式打开文件。
代码:
/*创建一个保存PCM数据的文件*/
if ((pcm_data_file = fopen(argv[1], "wb")) == NULL)
{printf("无法创建%s音频文件.\n", argv[1]);exit(1);
}
printf("用于录制的音频文件已打开.\n");参数:
argv[1]:程序执行时传递的参数,例./voice record.cpm,则该参数为"record.cpm"
"wb":只写打开或新建一个二进制文件,只允许写数据。
硬件控制
sysfs文件系统
在 Linux 系统下,一切皆文件!应用层如何操控底层硬件,同样也是通过文件 I/O 的方式来实现。本项目的硬件控制都是通过sysfs文件系统实现对LED和蜂鸣器的控制。
在嵌入式Linux开发中,sysfs文件系统通常被用来访问硬件资源,例如GPIO、I2C、SPI等外设,可以通过sysfs文件系统的接口来控制和读取硬件设备的状态信息。因此,sysfs文件系统对于嵌入式Linux开发非常重要,几乎所有的嵌入式Linux系统都会支持sysfs文件系统。
sysfs 文件系统挂载在/sys 目录下,启动开发板后可以到/sys目录查看。/sys下不同的子目录。
/sys
目录下包含了很多子目录,每个子目录都代表一个系统设备或内核模块,其中常见的子目录包括:
- block:块设备相关的信息,例如硬盘、光驱等。
- bus:系统总线相关的信息,例如USB、PCI、I2C等。
- class:设备类型相关的信息,例如输入设备、网络设备等。
- dev:与设备文件相关的信息,例如设备号、设备名称等。
- firmware:硬件固件相关的信息,例如BIOS、驱动程序等。
- fs:文件系统相关的信息,例如文件系统挂载状态等。
- kernel:内核相关的信息,例如内核版本、内核命令行参数等。
- module:内核模块相关的信息,例如已加载的内核模块等。
- power:电源管理相关的信息,例如电量、电源状态等。
- sys:系统信息相关的信息,例如CPU信息、内存信息等。
这些子目录下包含了许多虚拟文件和目录,通过这些文件和目录可以方便地访问内核数据结构和设备信息,从而实现对设备的控制和监控。
在sysfs文件系统中,一个硬件设备为一个目录,设备的属性则为文件。
在正点原子STM32MP157中,LED和蜂鸣器对应的设备目录均为/sys/class/leds/。
LED的触发方式控制文件为/sys/class/leds/user-led/trigger,LED亮度控制文件为/sys/class/leds/user-led/brightness。
蜂鸣器的触发方式控制未见为/sys/class/leds/beep/trigger,蜂鸣器开关控制文件为/sys/class/leds/beep/brightness。
数据解析和控制
本项目使用strstr函数对识别的结果进行判断,判断识别结果中是否包含"灯"、“蜂鸣器"字符串,有的话再判断结果中包含"开"还是"关”。
如识别的结果为"灯打开"或"开灯",程序将修改向LED灯的触发方式文件写入none(无触发),向LED亮度控制文件写入"1",这样就实现了LED灯的点亮
函数:
函数原型:
char *strstr(const char *haystack, const char *needle) haystack -- 要被检索的 C 字符串.
needle -- 在 haystack 字符串内要搜索的子字符串。作用:
查看原字符串中是否存在子字符串,不存在返回NULL
部分代码:
void Voice_Controll(char result[])
{/*检测语音和灯的控制有关*/if(strstr(result,"灯")!=NULL){if(strstr(result,"开")!=NULL && strstr(result,"关")!=NULL)return;else if(strstr(result,"开")!=NULL) Led_Controll(1); else if(strstr(result,"关")!=NULL) Led_Controll(0); }/*检测语音和蜂鸣器的控制有关*/......
}void Led_Controll(int ONOFF)
{int fd1,fd2;/*打开LED触发文件*/fd1 = open(LED_TRIGGER, O_WRONLY);if (0 > fd1) {perror("open error");exit(-1);}/*打开LED开关文件*/fd2 = open(LED_BRIGHTNESS, O_WRONLY);if (0 > fd2) {perror("open error");exit(-1);}/*根据传递参数控制LED*/if(ONOFF == 1){write(fd1, "none", 4); //先将触发模式设置为 nonewrite(fd2, "1", 1); //点亮 LEDprintf("LED已打开!\n");}else{write(fd1, "none", 4); //先将触发模式设置为 nonewrite(fd2, "0", 1); //熄灭 LEDprintf("LED已关闭!\n");}/*关闭文件*/close(fd1);close(fd2);
}
多线程
函数:
函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)thread -- 传出参数,保存系统为我们分配好的线程 ID
attr -- 通常传 NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
start_routine -- 函数指针,指向线程主函数,该函数运行结束,则线程结束。
arg -- 线程主函数执行期间所使用的参数。作用:
创建一个新线程。函数原型:
int int truncate(const char *path,off_t length);参数:
path -- 文件路径名。
length -- 截断长度,若文件大小>length大小,额外的数据丢失。若文件大小<length大小,那么,这个文件将会被扩展,扩展的部分将补以null,也就是‘\0’。作用:
截断或扩展文件。
代码:
/*创建子线程检测按键是否按下*/
pthread_t tid;
ret = pthread_create(&tid, NULL, button_tfn, NULL);
if (ret != 0) perror("pthread_create failed");void *button_tfn(void *arg)
{struct input_event in_ev = {0};int fd;int value = -1;/*打开按键事件对应的文件*/if (0 > (fd = open("/dev/input/event1", O_RDONLY))){perror("open error");exit(-1);}while(1){/*循环读取数据*/if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event))){perror("read error");exit(-1);}if (EV_KEY == in_ev.type && in_ev.code == 114)//114为KEY0 { /*按键事件*/switch (in_ev.value){/*KEY0松开*/case 0:/*** 1.更新按键状态为松开* 2.延时等待主循环判断,否则可能出现主循环先判断标志位为1而出现PCM设备停止还在继续读数据* 3.停止PCM设备*/key_flag_now = 0;sleep(1);snd_pcm_drop(capture_handle);break;/*KEY0按下*/case 1:/*** 1.清空文件,使文件从头开始写,等于重新录制音频* 2.同样注意顺序,先使设备恢复进入准备状态,避免出现主循环先检测到标志位为1而读取声卡设备* 3.更新按键状态为按下*/truncate(pcm_file_name,1);snd_pcm_prepare(capture_handle);key_flag_now = 1;break;}}else if(EV_KEY == in_ev.type && in_ev.code == 115)//115为KEY1{/*按键事件*/switch (in_ev.value){/*KEY1按下*/case 1:/*退出程序*/exit_program();break;}}}
}
主循环
主循环内判断声卡设备状态是否改变(按键状态决定),若当前声卡为运行状态则进行音频采集,若当前声卡为停止状态则调用API进行识别。
while (1)
{/*判断按键状态是否更新*/if(key_flag_now != key_flag_old){/*视当前状态为旧状态*/key_flag_old = key_flag_now;/*若按键按下*/if(key_flag_now == 1)printf("开始采集音频数据...\n");/*若按键松开*/else{printf("采集结束!\n");/*调用API进行识别*/run_asr(&config, token,result);/*对识别的结果进行处理*/Voice_Controll(result);printf("请长按KEY0按键开始采集音频数据!单击KEY1退出程序!\n");}}/*若按键按下*/if(key_flag_now == 1){/*从声卡设备读取一帧音频数据:2048字节*/ret = snd_pcm_readi(capture_handle, buffer, buffer_frames);if(0 > ret){printf("从音频接口读取失败(%s)\n", snd_strerror(ret));exit(1);}/*写数据到文件: 音频的每帧数据样本大小是16位=2个字节*/fwrite(buffer, (ret * AUDIO_CHANNEL_SET), frame_byte, pcm_data_file);}
}
实现效果及注意事项
实现效果
如图所示长按KEY0按键开始音频录制,松开即音频录制结束,再调用百度语言API进行识别,并向用户展示识别的结果以及实现对硬件的控制。之后用户可自行选择继续识别或退出程序。
注意事项
该程序在声卡不进行录音时是将声卡设备给停止工作了的,在停止声卡设备前需要加入一小段的延时等待,若不添加延时等待,可能会出现子线程使声卡设备停止的同时主线程在读取声卡设备,从而导致下图中出现的错误:
源代码(转载请注明出处)
相关文章:

STM32MP157-Linux音频应用编程-简易语音助手
文章目录前言STM32MP157简易语音助手alsa-lib简介:移植alsa-lib库:libcurl库简介:移植libcurl库:API调用修改asrmain.c文件修改token.c文件录音文件IO打开音频文件硬件控制sysfs文件系统数据解析和控制多线程主循环实现效果及注意…...
Python-OpenCV图像处理:学习图像算术运算,如加减法、图像混合、按位运算,以及如何实现它们
目录 目标 图像添加 图像混合算法 按位运算 目标 学习对图像的几种算术运算,如加法、减法、位运算等。了解这些功能:cv.add()、...

并发编程——ReentrantLock
如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:耶瞳空间 一:基本介绍 从Java 5开始,引入了一个高级的处理并发的java.util.concurrent包,它提供了大量更高级的并发功能,能大大简化多线程程序的编写…...

English Learning - L2 第 3 次小组纠音 [ʌ] [ɒ] [ʊ] [ɪ] [ə] [e] 2023.3.4 周六
English Learning - L2 第 3 次小组纠音 [ʌ] [ɒ] [ʊ] [ɪ] [ə] [e] 2023.3.4 周六共性问题小元音 [ʌ]小元音 [ɒ]小元音 [ʊ]小元音 [ɪ]小元音 [ə]小元音 [e]我的发音问题纠音过程共性问题 小元音 [ʌ] 口型容易偏大 解决办法:因为嘴角没有放松,…...

STM32之关门狗
看门狗介绍在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入…...

Apollo控制部分1-- ControlComponent组件介绍
Apollo控制部分1-- ControlComponent组件介绍摘要一、ControlComponent1、启动文件解析2、ControlComponent()组件函数解析1)ControlComponent::ControlComponent() 构造函数2)ControlComponent::Init() 初始化函数(执行一次)3&am…...

0626-0631韩顺平Java Buffered字节处理流 学习笔记
如何去构建字节流package com.hspedu.outputstream_;import java.io.*;/*** author abner* version 1.0*/ public class BufferedCopy02 {public static void main(String[] args) {String srcFilePath "D:\\Users\\Pictures\\Camera Roll\\Pierre-Auguste_Renoir,_Le_Mo…...

【网络】序列化和反序列化
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
【代码随想录训练营】【Day32】第八章|贪心算法|122.买卖股票的最佳时机II |55. 跳跃游戏|45.跳跃游戏II
买卖股票的最佳时机II 题目详细:LeetCode.122 买卖股票的最佳时机,怎么都能够想出来个思路,假如我们每天都能预知明天的股票是涨是降,那么贪心策略就是在涨之前买股票,在降的前一天卖掉,这就是买卖股票的…...
constexpr 和 常量表达式
👀👀常量表达式 常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。 那么是什么来就决定是不是常量表达式呢?一个对象是不是常量表达式主要…...

Vue响应式原理————Object.defineProperty()和proxy的用法分享
Vue框架一个比较核心的功能就是我们的数据是响应式的,这样我们在修改数据的时候,页面会自动帮我们更新,那么想要实现这个功能就要实现对一个数据的劫持,即在取值和设置值的同时我们能够检测到即数据劫持。vue2响应式的实现原理所依…...
CSDN 编程竞赛三十四期题解
竞赛总览 CSDN 编程竞赛三十四期:比赛详情 (csdn.net) 本期的题目和第三十一期竞赛的题目竟然高度重合,真不知道该写点什么了。 不过,上次那道测试数据有bug的题已经修复了,答题过程挺顺利的,没有遇到新的问题。 竞…...
C#教程06 运算符
文章目录 一、算术运算符加法运算符(+)减法运算符(-)乘法运算符(*)除法运算符(/)二、逻辑运算符与运算符(&&)或运算符(||)非运算符(!)三、比较运算符等于运算符(==)大于运算符(>)小于运算符(<)大于等于运算符(>=)小于等于运算符(<=…...
软测入门(六)pytest单元测试
pytest pytest是python的一种单元测试框架,同自带的unit test测试框架类似,但pytest更简洁高效。 单元测试: 测试 函数、类、方法能不能正常运行测试的结果是否符合我们的预期结果 安装 pip install -U pytest基本使用 通过pytest包使用…...
经典分类模型回顾5—DenseNet实现图像分类(matlab)
DenseNet,全称为Densely Connected Convolutional Networks,中文名为密集连接卷积网络,是由李沐等人在2017年提出的一种深度神经网络架构。 DenseNet旨在解决深度神经网络中的梯度消失问题和参数数量过多的问题,通过构建密集连接…...

基于flask+bootstrap+echarts+mysql的鱼村小馆订餐后台管理系统
📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言Ὅ…...
Spark使用Log4j将日志发送到Kafka
文章目录自定义KafkaAppender修改log4j.properties配置启动命令配置添加参数启动之后可以在Kafka中查询发送数据时区问题-自定义实现JSONLayout解决自定义JSONLayout.java一键应用可能遇到的异常ClassNotFoundException: xxx.KafkaLog4jAppenderUnexpected problem occured dur…...

c++类与对象整理(上)
目录 1.类的引入 2.类的定义 3.类的访问限定符及封装 1)访问限定符 2)封装 4.类的作用域 5.类的实例化 6.类的对象大小的计算 1)类对象的存储方式 2)内存对齐和大小计算 编辑 7.类成员函数的this指针 1)…...
Docker学习(十九)什么是镜像的元数据?
在 Docker 中,镜像的元数据是指与镜像相关的所有信息,包括镜像的名称和标签、作者、描述、创建日期、环境变量、命令等。这些信息都是通过 Dockerfile 或命令行创建和指定的。 镜像的元数据被存储在 Docker Registry 中,并在使用 docker pull…...

Python如何获取弹幕?给你介绍两种方式
前言 弹幕可以给观众一种“实时互动”的错觉,虽然不同弹幕的发送时间有所区别,但是其只会在视频中特定的一个时间点出现,因此在相同时刻发送的弹幕基本上也具有相同的主题,在参与评论时就会有与其他观众同时评论的错觉。 在国内…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...

Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...