基于全志Orangepi Zero 2 的语音刷抖音项目

news2024/9/21 22:29:43

目录

一、硬件连接

二、项目实现功能

三、SU-03T语音模块的配置和烧录

3.1 创建产品:

3.2 设置PIN引脚为串口模式:

3.3 设置唤醒词:

3.4 设置指令语句:

3.5 设置控制详情:

3.6 发音人配置:

3.7 其他设置:

3.7 下载SDK并烧录到语音模块SU-03T:

四、测试语音模块:

4.1 通过语音模块与串口助手连接测试语音模块:

4.2 用代码来测试语音模块:

五、将手机接入香橙派

5.1 查看手机是否接入:

5.2 ADB的认知和常用命令:

5.3 安装ADB:

5.4 设置手机权限:

5.5 使用shell指令来模拟手滑屏幕:

六、编程实现项目功能


一、硬件连接

二、项目实现功能

项目通过语音模块来输出指令控制手机刷抖音,可以实现:上一个、下一个、点赞等功能

  1. 开机播报少刷抖音多看书

  2. 当说出小布小布可以唤醒语音模块,模块回复我在呢

  3. 超过10S没有指令或者说出退下时,模块会进入到休眠模式,并且回复有需要再叫我

  4. 当说出下一个的时候,模块回复好的,并且通过串口发送指令给H616,让手机划到下一个视频

  5. 当说出上一个的时候,模块回复好的,并且通过串口发送指令给H616,让手机划到上一个视频

  6. 当说出点赞的时候,模块回复遵命,并且通过串口发送指令给H616,让手机点赞

  7. 当说出不看了的时候,模块回复拜拜,并且通过串口发送指令给H616,让手机锁屏

三、SU-03T语音模块的配置和烧录

3.1 创建产品:

3.2 设置PIN引脚为串口模式:

SU-03T语音模块的串口引脚是B6和B7,并设置相应的波特率

3.3 设置唤醒词:

3.4 设置指令语句:

3.5 设置控制详情:

3.6 发音人配置:

3.7 其他设置:

3.7 下载SDK并烧录到语音模块SU-03T:

 语音模块配置完成后,大约等待10-30分钟后,将语音模块生成的SDK下载到电脑中,用生成的SDK包中的烧录软件进行烧录:

四、测试语音模块:

4.1 通过语音模块与串口助手连接测试语音模块:

语音模块配置完成后,将语音模块与CH340串口助手链接,然后通过喊语音模块观察串口助手的指令

4.2 用代码来测试语音模块:

/*mySerialTool.c*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

/*打开指定的串口设备,并设置波特率*/
int mySerialOpen(const char *device, const int baud)
{
    struct termios options; // 串口配置参数
    speed_t myBaud;         // 波特率
    int status, fd;         // 状态和文件描述符

    //根据传入的波特率参数设置相应的波特率
    switch(baud){
        case 9600:
            myBaud = B9600;
            break;
        case 115200:
            myBaud = B115200;
            break;
        default:
            printf("不支持的波特率!\n");
            return -2;
    }  

    //打开串口设备
    if( (fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY)) == -1){
        printf("无法打开串口设备\n");
        return -1;
    }

     // 设置文件描述符的标志为读写模式
    fcntl(fd, F_SETFL, O_RDWR);

    // 获取当前串口配置
    tcgetattr(fd, &options);

    // 设置串口为原始模式,无特殊处理
    cfmakeraw(&options);
    // 设置输入波特率
    cfsetispeed(&options, myBaud);
    // 设置输出波特率
    cfsetospeed(&options, myBaud);

    // 清除标志位并设置数据格式
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB; // 无奇偶校验位
    options.c_cflag &= ~CSTOPB; // 1个停止位
    options.c_cflag &= ~CSIZE; // 清除数据位
    options.c_cflag |= CS8; // 设置为8位数据位
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 非规范模式,不执行输入处理
    options.c_oflag &= ~OPOST; // 输出处理

    // 设置读取时的超时和最小接收字符数
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 100; // 10秒超时

    // 应用串口配置
    tcsetattr(fd, TCSANOW, &options);

    // 使用ioctl设置串口的DTR和RTS信号
    ioctl(fd, TIOCMGET, &status);
    status |= TIOCM_DTR;
    status |= TIOCM_RTS;
    ioctl(fd, TIOCMSET, &status);

    // 短暂延时
    usleep(10000); // 10毫秒延时

    return fd; // 返回文件描述符
}

/*向指定的串口设备发送字符串*/
void mySerialSendString(const int fd, const char *str)
{
    int ret;

    ret = write(fd, str, strlen(str));            // 发送字符串
    if(ret == -1){
        printf("串口发送失败!\n");
        exit(-1);                                 // 发送失败,退出程序
    }
}

/*从指定的串口设备读取字符串*/
int mySerialReadString(const int fd, char *buffer)
{
    int n_read;

    n_read = read(fd, buffer, 32);                // 读取串口数据
    if(n_read == -1){
        printf("串口读取失败!\n");
        exit(-1);                                 // 读取失败,退出程序
    }
}

/*从指定的串口设备读取一个字符*/
char mySerialGetChar(const int fd)
{
    char x;

    if(read(fd, &x, 1) != 1){
        printf("串口读取失败!\n");
        exit(-1);                                 // 读取失败,退出程序
    }
    return x;
}
/*mySerial.h*/
/*
* @Author: <NAME> 打开指定的串口设备,并设置波特率
*
* @param device 串口设备名称,如"/dev/ttyUSB0"
* @param baud 波特率,如9600、115200等
* @return 成功返回文件描述符,失败返回-1
*/
int mySerialOpen(const char *device, const int baud);

/*
* @Author: 向指定的串口设备发送字符串
*
* @param fd 串口设备文件描述符
* @param str 要发送的字符串 
* @return 无
*/
void mySerialSendString(const int fd, const char *str);

/*
* @Author: 从指定的串口设备读取字符串
*
* @param fd 串口设备文件描述符              
* @param buffer 读取到的字符串存放的缓冲区      
* @return 读取到的字符串的长度,失败返回-1
*/
int mySerialReadString(const int fd, char *buffer);

/*
* @Author: 从指定的串口设备读取一个字符
*
* @param fd 串口设备文件描述符
* @return 读取到的字符,失败返回-1
*/
char mySerialGetChar(const int fd);
/*mySerialTest2.c*/
#include <stdio.h>         // 包含标准输入输出库头文件
#include <string.h>        // 包含字符串处理库头文件
#include <errno.h>         // 包含错误号头文件
#include <stdlib.h>        // 包含标准库头文件
#include <pthread.h>       // 包含线程库头文件
#include <unistd.h>        // 包含unistd库头文件,用于延时
#include "mySerial.h"      // 包含自定义串口通信头文件

int fd;                                                                                      // 串口文件描述符

void* readSerial()
{
    char cmd;

    while(1){
        cmd = mySerialGetChar(fd);                                                             // 读取串口数据    
        switch(cmd){
            case 'c':
                printf("上一个\n");            
                break;
            case 'h':
                printf("下一个\n");                                  
                break;
            case 'j':
                printf("点赞\n");                           
                break;
            case '^':
                printf("不看了\n");
                break;
        }
    }
}

int main(int argc, char **argv)
{
    char deviceName[32] = {'\0'};                                                                  // 设备名
    pthread_t readThread;                                                                         // 读取线程                                                                    // 发送线程   

    if(argc < 2){
        printf("Usage: %s /dev/ttyS?\n", argv[0]);
        return -1;
    }

    strcpy(deviceName, argv[1]);                                                               // 拷贝设备名
    if( (fd = mySerialOpen(deviceName, 115200)) == -1){                                        // 打开串口
        printf("open %s failed\n", deviceName);
    }

    pthread_create(&readThread, NULL, readSerial, NULL);                                       // 创建读取线程

    while(1){
        sleep(10);                                                                              // 延时10秒
    }
    return 0;
}

五、将手机接入香橙派

将我的手机接入到香橙派,使用一颗TYPE-C的USB线连接到全志的USB2.0接口上

5.1 查看手机是否接入:

dmesg	//查看手机是否接入

输入dmesg指令查看手机接入情况,可见我的OPPO手机已经接入了

5.2 ADB的认知和常用命令:

ADB(Android Debug Bridge)是一种用于与安卓设备通信和调试的命令行工具。它是Android开发工具包(Android SDK)的一部分,用于在开发过程中连接、控制和调试Android设备或模拟器。

ADB提供了一组命令,允许开发者在计算机上执行各种操作,包括安装和卸载应用程序、传输文件、执行Shell命令、查看设备日志等。使用ADB,开发者可以与设备交互,调试应用程序、分析问题并进行性能优化。

ADB可以通过USB连接或通过网络连接到Android设备。通过USB连接,开发者可以直接将设备连接到计算机,并通过ADB命令与设备进行通信。通过网络连接,开发者可以使用无线网络连接到设备,从而无需使用USB线缆。

需要注意的是,ADB在设备上需要开启开发者选项和USB调试模式才能正常使用。此外,ADB还提供了一些高级功能,如端口转发、截屏、录屏等,以支持更丰富的开发和调试需求。

总的来说,ADB是Android开发中非常重要的工具,它简化了与Android设备的通信和调试过程,为开发者提供了更好的开发环境和工作效率。

ADB(Android Debug Bridge)是一个用于在计算机和 Android 设备之间进行通信的命令行工具。它允许开发者执行各种设备操作,例如安装和调试应用程序、访问设备的 Shell、复制文件到设备或从设备复制文件等。ADB 是 Android SDK 的一部分,开发者可以使用它与 Android 设备进行交互。

以下是一些常用的ADB命令:

  • 连接设备:

adb devices
  • 安装应用:

adb install example.apk
  • 卸载应用:

adb uninstall com.example.package
  • 启动应用:

adb shell am start -n com.example.package/.MainActivity
  • 查看设备信息:

adb shell getprop
  • 复制文件到设备:

adb push localfile.txt /sdcard/
  • 从设备复制文件:

adb pull /sdcard/remotefile.txt
  • 启动设备shell:

adb shell
  • 查看日志:

adb logcat

这只是一些常见的 ADB 命令示例,ADB 提供了更多的功能,可以帮助开发者进行 Android 应用程序的开发、调试和测试。请注意,使用 ADB 前需要确保 Android 设备已启用开发者选项和 USB 调试。

5.3 安装ADB:

由于安卓手机的底层也是用Linux系统来操作的,所以可以通过香橙派来直接进入控制手机shell的界面,但需要先安装adb工具

sudo apt-get install adb

安装完成之后,执行adb devices指令连接设备,但是好像发现权限不太对,因此需要在手机上设置权限

5.4 设置手机权限:

  • 报错的本质原因是香橙派系统还不支持USB设备的热拔插UDEV的机制

  • 解决办法:/etc/udev/rules.d 文件夹下创建规则文件:

cd /etc/udev/rules.d/
sudo vim 51-android.rules	

 在文件中添加内容:SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"

然后重新拔插手机,然后再次执行adb devices指令:(前提手机已经代开开发者模式中的USB调试)

 然后输入adb shell启动设备shell:

连接手机成功!!!

5.5 使用shell指令来模拟手滑屏幕:

现在可以成功的连入手机内部的系统,关键就在于对于滑动或点击屏幕的指令模拟了:

  • adb shell input swipe <起始x坐标> <起始y坐标> <结束x坐标> <结束y坐标> <滑动持续时间ms>

  • adb shell input keyevent <按键事件的常量>

adb shell input swipe 540 1300 540 500 500 															//下滑
adb shell input swipe 540 500 540 1300 500 															//上滑
adb shell "seq 4 | while read i;do input tap 350 1050 & input tap 350 1050 &sleep 0.05;done;" 		//点赞
adb shell input keyevent 26 																		//锁屏

六、编程实现项目功能

/*mySerialTool.c*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

/*打开指定的串口设备,并设置波特率*/
int mySerialOpen(const char *device, const int baud)
{
    struct termios options; // 串口配置参数
    speed_t myBaud;         // 波特率
    int status, fd;         // 状态和文件描述符

    //根据传入的波特率参数设置相应的波特率
    switch(baud){
        case 9600:
            myBaud = B9600;
            break;
        case 115200:
            myBaud = B115200;
            break;
        default:
            printf("不支持的波特率!\n");
            return -2;
    }  

    //打开串口设备
    if( (fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY)) == -1){
        printf("无法打开串口设备\n");
        return -1;
    }

     // 设置文件描述符的标志为读写模式
    fcntl(fd, F_SETFL, O_RDWR);

    // 获取当前串口配置
    tcgetattr(fd, &options);

    // 设置串口为原始模式,无特殊处理
    cfmakeraw(&options);
    // 设置输入波特率
    cfsetispeed(&options, myBaud);
    // 设置输出波特率
    cfsetospeed(&options, myBaud);

    // 清除标志位并设置数据格式
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB; // 无奇偶校验位
    options.c_cflag &= ~CSTOPB; // 1个停止位
    options.c_cflag &= ~CSIZE; // 清除数据位
    options.c_cflag |= CS8; // 设置为8位数据位
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 非规范模式,不执行输入处理
    options.c_oflag &= ~OPOST; // 输出处理

    // 设置读取时的超时和最小接收字符数
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 100; // 10秒超时

    // 应用串口配置
    tcsetattr(fd, TCSANOW, &options);

    // 使用ioctl设置串口的DTR和RTS信号
    ioctl(fd, TIOCMGET, &status);
    status |= TIOCM_DTR;
    status |= TIOCM_RTS;
    ioctl(fd, TIOCMSET, &status);

    // 短暂延时
    usleep(10000); // 10毫秒延时

    return fd; // 返回文件描述符
}

/*向指定的串口设备发送字符串*/
void mySerialSendString(const int fd, const char *str)
{
    int ret;

    ret = write(fd, str, strlen(str));            // 发送字符串
    if(ret == -1){
        printf("串口发送失败!\n");
        exit(-1);                                 // 发送失败,退出程序
    }
}

/*从指定的串口设备读取字符串*/
int mySerialReadString(const int fd, char *buffer)
{
    int n_read;

    n_read = read(fd, buffer, 32);                // 读取串口数据
    if(n_read == -1){
        printf("串口读取失败!\n");
        exit(-1);                                 // 读取失败,退出程序
    }
}

/*从指定的串口设备读取一个字符*/
char mySerialGetChar(const int fd)
{
    char x;

    if(read(fd, &x, 1) != 1){
        printf("串口读取失败!\n");
        exit(-1);                                 // 读取失败,退出程序
    }
    return x;
}
/*mySerial.h*/
/*
* @Author: <NAME> 打开指定的串口设备,并设置波特率
*
* @param device 串口设备名称,如"/dev/ttyUSB0"
* @param baud 波特率,如9600、115200等
* @return 成功返回文件描述符,失败返回-1
*/
int mySerialOpen(const char *device, const int baud);

/*
* @Author: 向指定的串口设备发送字符串
*
* @param fd 串口设备文件描述符
* @param str 要发送的字符串 
* @return 无
*/
void mySerialSendString(const int fd, const char *str);

/*
* @Author: 从指定的串口设备读取字符串
*
* @param fd 串口设备文件描述符              
* @param buffer 读取到的字符串存放的缓冲区      
* @return 读取到的字符串的长度,失败返回-1
*/
int mySerialReadString(const int fd, char *buffer);

/*
* @Author: 从指定的串口设备读取一个字符
*
* @param fd 串口设备文件描述符
* @return 读取到的字符,失败返回-1
*/
char mySerialGetChar(const int fd);
/*mySerialTest.c*/
#include <stdio.h>         // 包含标准输入输出库头文件
#include <string.h>        // 包含字符串处理库头文件
#include <errno.h>         // 包含错误号头文件
#include <stdlib.h>        // 包含标准库头文件
#include <pthread.h>       // 包含线程库头文件
#include <unistd.h>        // 包含unistd库头文件,用于延时
#include "mySerial.h"      // 包含自定义串口通信头文件

int fd;                                                                                      // 串口文件描述符

void* readSerial()
{
    char cmd;

    while(1){
        cmd = mySerialGetChar(fd);                                                             // 读取串口数据    
        switch(cmd){
            case 'c':
                printf("上一个\n");
                system("adb shell input swipe 540 500 540 1300 100");                       
                break;
            case 'h':
                printf("下一个\n");
                system("adb shell input swipe 540 1300 540 500 100");                                     
                break;
            case 'j':
                printf("点赞\n");
                system("adb shell \"seq 4 | while read i;do input tap 350 1050 & input tap 350 1050 &sleep 					0.05;done;\"");   // 点赞                                   
                break;
            case '^':
                printf("不看了\n");
                system("adb shell input keyevent 26");                                              // 不看了 
                break;
        }
    }
}

int main(int argc, char **argv)
{
    char deviceName[32] = {'\0'};                                                                  // 设备名
    pthread_t readThread;                                                                         // 读取线程                                                                    // 发送线程   

    if(argc < 2){
        printf("Usage: %s /dev/ttyS?\n", argv[0]);
        return -1;
    }

    strcpy(deviceName, argv[1]);                                                                 // 拷贝设备名
    if( (fd = mySerialOpen(deviceName, 115200)) == -1){                                          // 打开串口
        printf("open %s failed\n", deviceName);
    }

    pthread_create(&readThread, NULL, readSerial, NULL);                                       // 创建读取线程

    while(1){
        sleep(10);                                                                                // 延时10秒
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1908119.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第二证券股市知识:股票填权是怎么回事?利好还是利空?

1、股票填权的含义 股票填权是指在除权除息之后的一段时刻内&#xff0c;假设多数投资者看好该个股&#xff0c;股票的价格超过除权除息的基准价就叫做填权。上市公司假设能持续分红&#xff0c;就会向市场传递积极信号&#xff0c;招引更多投资者买入&#xff0c;越来越多的投…

我“硬刚”mmkv开源库对于版本号的定义赢啦!

我“硬刚”mmkv开源库胜利啦&#xff01; 前情是这个帖子https://blog.csdn.net/jzlhll123/article/details/139917169 之前项目中将mmkv1.3.4升级到1.3.5或者1.3.6&#xff0c;就从firebase后台上看到crash。 java.lang.UnsatisfiedLinkError: dlopen failed: library “libmm…

数据融合工具(3)国家基本比例尺地形图分幅计算

情景再现&#xff0c;呼叫小编 数据获取和使用过程中&#xff0c;经常听到一个名词“分幅图幅号”…… 你的数据是按多大比例尺分幅的&#xff1f;我不知道&#xff0c;就一些字母和数值。 你把G47E018018范围内的数据裁剪提供&#xff0c;这个范围是啥&#xff1f; 你把镶嵌…

VTD的RDB介绍,从入门到放弃

文章目录 前言一、二、常见的RDB数据类型1、RDB_OBJECT_STATE_BASE_t2、RDB_OBJECT_STATE_EXT_t3、RDB_OBJECT_STATE_t4、RDB_SENSOR_OBJECT_t5、RDB_COORD_t6 RDB_GEOMETRY_t7、RDB_MSG_ENTRY_HDR_t 三、疑惑的问题点&#xff1a;1、在RDB_OBJECT_STATE_EXT_t中这两个的区别是…

Python提取视频文案

Python提取视频文案 1、背景描述2、视频转音频3、音频转文字 1、背景描述 在多媒体应用中&#xff0c;视频是一个信息量巨大的载体。然而&#xff0c;有时我们需要从视频中提取语音并转换为文本&#xff0c;以用于文本分析和机器学习训练 其中主要涉及到两个过程&#xff1a;视…

提高LabVIEW软件的健壮性

提高LabVIEW软件的健壮性&#xff0c;即增强其在各种操作条件下的可靠性和稳定性&#xff0c;是开发过程中非常重要的一环。健壮的软件能够在面对意外输入、极端环境和系统故障时依然表现出色&#xff0c;确保系统的连续性和可靠性。以下是详细的方法和策略&#xff0c;从多个角…

容易涨粉的视频素材在哪找啊?爆款涨粉的视频素材网站有这几个

亲爱的读者&#xff0c;大家好&#xff01;今天我们要探讨一个至关重要的问题&#xff1a;在充满竞争的视觉时代&#xff0c;如何使自己的短视频脱颖而出并吸引更多粉丝&#xff1f;关键在于使用那些既酷炫又高质量的视频素材&#xff01;下面就向大家推荐几个顶级视频素材网站…

动态粒子发射特效404网站HTML源码

源码介绍 动态粒子发射404网站HTML源码&#xff0c;粒子内容可以进行修改&#xff0c;默认是4&#xff0c;0数字还有一个页面不存在英文&#xff0c;可以自行修改&#xff0c;喜欢的朋友可以拿去使用&#xff0c;源码是html&#xff0c;记事本打开修改即可&#xff0c;鼠标双击…

独立站外链建设中常见的错误是什么?

最大的错误毫无疑问就是低质量的网站链接会对SEO产生负面影响&#xff0c;实际上&#xff0c;外链只有有效和无效之分&#xff0c;低质量的外链不会带来负面影响。否则&#xff0c;通过大量发低质量外链就可以搞垮竞争对手的网站了。但事实并非如此&#xff0c;真能这么做就乱套…

鸿蒙小练习

bean对象 export class BannerImage{id:numberurl:stringtargetUrl:stringproductId:numberconstructor(id: number, url: string, targetUrl: string, productId: number) {this.id idthis.url urlthis.targetUrl targetUrlthis.productId productId} }export class d…

Spring Boot集成rmi快速入门demo

1.什么是rmi&#xff1f; RMI&#xff08;Remote Method Invocation&#xff09;即远程方法调用&#xff0c;是分布式编程中的一个基本思想。实现远程方法调用的技术有很多&#xff0c;比如CORBA、WebService&#xff0c;这两种都是独立于各个编程语言的。 而Java RMI是专为Ja…

Redis原理-数据结构

Redis原理篇 1、原理篇-Redis数据结构 1.1 Redis数据结构-动态字符串 我们都知道Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不过Redis没有直接使用C语言中的字符串&#xff0c;因为C语言字符串存…

高通开发系列 - 使用QFIL工具单刷某个镜像文件

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回:专栏总目录 目录 背景过程记录背景 有时候设备中刷的是user版本,无法使用fastboot刷单个镜像,这个时候该怎么办呢? 要解决在user…

JavaScript中,正则表达式所涉及的api,解析、实例和总结

JS中正则的api包括以下&#xff1a; String#searchString#splitString#matchString#replaceRegExp#testRegExp#exec 1. String#search 查找输入串中第一个匹配正则的index&#xff0c;如果没有匹配的则返回-1。g修饰符对结果无影响 var string "abbbcbc"; var r…

2.硬盘和内存区别

2.2 磁盘比内存慢几万倍&#xff1f; 存储器方面的设备&#xff0c;分类比较多&#xff0c;那我们肯定不能只买一种存储器&#xff0c;比如你除了要买内存&#xff0c;还要买硬盘&#xff0c;而针对硬盘我们还可以选择是固态硬盘还是机械硬盘。 相信大家都知道内存和硬盘都属…

Spring的AOP进阶。(AOP的通知类型、通知顺序、切入点表达式和连接点。)

3. AOP进阶 AOP的基础知识学习完之后&#xff0c;下面我们对AOP当中的各个细节进行详细的学习。主要分为4个部分&#xff1a; 通知类型通知顺序切入点表达式连接点 我们先来学习第一部分通知类型。 3.1 通知类型 在入门程序当中&#xff0c;我们已经使用了一种功能最为强大…

物理层与数据通信基础:构建稳定网络的关键

本章主要讨论物理层的基本概念、数据通信的基础知识、几种常用的信道复用技术以及互联网接入技术。对于具备通信基础知识的读者&#xff0c;可以有选择地学习本章内容。 本章重点内容 物理层的任务数据通信的基本概念常用的信道复用技术常用的互联网接入技术 2.1 物理层的基…

产品经理技能揭秘:如何巧妙启发需求,引领市场新潮流

文章目录 引言一、需求启发的定义二、需求启发的艺术三、需求启发的重要性四、需求启发的流程五、需求启发的问题与挑战内部自身的问题与挑战&#xff1a;挑战一&#xff1a;知识的诅咒挑战二&#xff1a;做与定义的不同挑战三&#xff1a;沟通障碍挑战四&#xff1a;需求变更频…

LabVIEW在半导体自动化测试中的应用

半导体制造的复杂性和精密度要求极高&#xff0c;每一个生产步骤都需要严格的控制和监测。自动化测试设备在半导体制造中起到了关键作用&#xff0c;通过精密测量和数据分析&#xff0c;确保产品质量和生产效率。本文介绍如何使用LabVIEW结合研华硬件&#xff0c;开发一个用于半…

磁致伸缩液位计的应用领域

磁致伸缩液位计作为一种高精度、高稳定性的液位测量设备&#xff0c;在众多行业中都有着广泛的应用。接下来&#xff0c;我们将从多个角度详细探讨磁致伸缩液位计在不同领域的应用情况。 石油化工行业 在石油化工行业中&#xff0c;磁致伸缩液位计主要用于储罐、反应器和管道等…