当前位置: 首页 > news >正文

STM32使用PID调速

STM32使用PID调速

PID原理

image-20230824191607500

PID算法是一种闭环控制系统中常用的算法,它结合了比例(P)、积分(I)和微分(D)三个环节,以实现对系统的控制。它的目的是使

控制系统的输出值尽可能接近预期的目标值。

在PID算法中,控制器通过不断地测量实际输出值和目标值之间的误差,并使用比例、积分和微分部分的控制参数来调整控制系统的输出

值。比例部分根据误差的大小进行控制,其输出与误差成正比。积分部分根据误差的历史累积值进行控制,其输出与误差积分的结果成正

比。微分部分根据误差的变化率进行控制,其输出与误差变化率成正比。

将这三个部分组合起来,就得到了PID算法。PID控制器不断地对系统进行测量和调整,直到实际输出值接近目标值为止。

连续性公式
u ( t ) = K p ∗ e ( t ) + K i ∗ ∫ 0 t e ( t ) d t + k d ∗ d e ( t ) d t u(t)=K_{p}*e(t)+K_{i}*\int_{0}^{t} e(t)dt+k{d}*\frac{de(t)}{dt} u(t)=Kpe(t)+Ki0te(t)dt+kddtde(t)
离散性公式
u ( t ) = K p ∗ e ( t ) + K i ∗ ∑ n = 0 t e ( n ) + k d ∗ [ e ( t ) − e ( t − 1 ) ] u(t)=K_{p}*e(t)+K_{i}*\sum_{n=0}^{t} e(n)+k{d}*[e(t)-e(t-1)] u(t)=Kpe(t)+Kin=0te(n)+kd[e(t)e(t1)]

  • 比例系数Kp:
    比例系数Kp的作用是根据当前误差的大小来调整控制器的输出。Kp越大,控制器对误差的灵敏度越高,系统的响应速度越快,但可能会出现过大的超调。Kp越小,控制器对误差的灵敏度越低,系统的响应速度越慢,但系统的稳定性较好。(快)
  • 积分系数Ki:
    积分系数Ki的作用是根据误差的历史累积值来调整控制器的输出。Ki越大,控制器对误差的累积量越大,系统的稳态误差消除越快,但可能会出现过大的超调。Ki越小,控制器对误差的累积量越小,系统的稳态误差消除越慢,但系统的稳定性较好。(准)
  • 微分系数Kd:
    微分系数Kd的作用是根据误差的变化率来调整控制器的输出。Kd越大,控制器对误差变化率的灵敏度越高,系统的响应速度越快,但可能会出现过大的超调。Kd越小,控制器对误差变化率的灵敏度越低,系统的响应速度越慢,但系统的稳定性较好。(稳)

PID使用

在工程文件中新建

pid.h

//pid.h
#ifndef __BSP_PID_H
#define	__BSP_PID_H
#include "stm32f1xx.h"
#include "usart.h"
#include <stdio.h>
#include <stdlib.h>
#include "tim.h"/*pid*/
typedef struct
{float target_val;float actual_val;float err;float err_last;float err_sum;float Kp,Ki,Kd;
}PID_struct;void PID_Init(PID_struct *pid);
float P_realize(PID_struct * pid, float actual_val);
float PI_realize(PID_struct * pid, float actual_val);
float PID_realize(PID_struct * pid, float actual_val);#endif

结构体中储存pid的参数目标值、当前值、误差、kp、ki、kd等等

pid.c

//pid.c
#include "pid.h"void PID_Init(PID_struct *pid)
{printf("PID_Init begin \n");pid->target_val=1.0;pid->actual_val=0.0;//误差pid->err=0.0;pid->err_last=0.0;pid->err_sum=0.0;//需要自己调节pid->Kp = 120.0;  //快pid->Ki = 5.0;   //准pid->Kd = 0.3;	//稳
}
float P_realize(PID_struct * pid, float actual_val)
{pid->actual_val = actual_val;pid->err = pid->target_val - pid->actual_val;pid->actual_val = pid->Kp * pid->err;return pid->actual_val;
}float PI_realize(PID_struct * pid, float actual_val)
{pid->actual_val = actual_val;pid->err = pid->target_val - pid->actual_val;pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum;return pid->actual_val;
}float PID_realize(PID_struct * pid, float actual_val)
{pid->actual_val = actual_val;pid->err = pid->target_val - pid->actual_val;pid->err_sum += pid->err;pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum + pid->Kd*(pid->err-pid->err_last);pid->err_last = pid->err;return pid->actual_val;
}

一共有四个函数分别为PID初始化、P调节、PI调节、PID调节

传入参数为PID结构体,和编码器测的速度

返回值为实际PWM值

使用main.c

#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
#include "stdio.h"
#include "motor.h"
#include "pid.h"
#include "oled.h"
/* USER CODE END Includes */short Enc1_cnt = 0;
short Enc2_cnt = 0;
float motor1_speed = 0.00;
float motor2_speed = 0.00;
int PWM_MAX = 1000, PWM_MIN = -1000;
PID_struct motor1_pid;
PID_struct motor2_pid;
int motor1_pwm, motor2_pwm;
char oledBuf[20];void SystemClock_Config(void);int main(void)
{HAL_Init();SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM3_Init();MX_USART1_UART_Init();MX_TIM2_Init();MX_TIM4_Init();/* USER CODE BEGIN 2 */HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);HAL_TIM_Base_Start_IT(&htim2);HAL_TIM_Base_Start_IT(&htim4);//PID初始化PID_Init(&motor1_pid);PID_Init(&motor2_pid);OLED_Init();OLED_ColorTurn(0);OLED_DisplayTurn(0);OLED_Clear();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){motor1_pwm = PID_realize(&motor1_pid, motor1_speed);motor2_pwm = PID_realize(&motor2_pid, motor2_speed);Load_PWM(motor1_pwm, motor2_pwm);Enc1_cnt = -(short)__HAL_TIM_GET_COUNTER(&htim2);Enc2_cnt = (short)__HAL_TIM_GET_COUNTER(&htim4);motor1_speed = (float)Enc1_cnt*100/45/11/4;motor2_speed = (float)Enc2_cnt*100/45/11/4;printf("Enc1_cnt: %d\r\n", Enc1_cnt);printf("Enc2_cnt: %d\r\n", Enc2_cnt);printf("motor1_speed: %.3f\r\n", motor1_speed);printf("motor2_speed: %.3f\r\n", motor2_speed);//OLED显示sprintf(oledBuf, "left_speed :%.3f",motor1_speed);OLED_ShowString(0, 40, (u8*)oledBuf, 12);sprintf(oledBuf, "right_speed:%.3f",motor2_speed);OLED_ShowString(0, 52, (u8*)oledBuf, 12);OLED_Refresh();__HAL_TIM_SET_COUNTER(&htim2, 0);__HAL_TIM_SET_COUNTER(&htim4, 0);HAL_Delay(10);}}

匿名上位机显示波形

匿名上位机下载

匿名上位机通信协议可参考这篇文章匿名上位机V7.12协议编程(基于STM32F407+CubeMX+UART外设通信)

使用

新建ano_upper.h

#ifndef STM32_ANO_UPPER_H
#define STM32_ANO_UPPER_H
#include "main.h"
#include "usart.h"//数据拆分宏定义,在发送大于1字节的数据类型时,比如int16、float等,需要把数据拆分成单独字节进行发送#define BYTE0(dwTemp) ( *( (char *)(&dwTemp) ) ) /*!< uint32_t 数据拆分 byte0 */
#define BYTE1(dwTemp) ( *( (char *)(&dwTemp) + 1) ) /*!< uint32_t 数据拆分 byte1 */
#define BYTE2(dwTemp) ( *( (char *)(&dwTemp) + 2) ) /*!< uint32_t 数据拆分 byte2 */
#define BYTE3(dwTemp) ( *( (char *)(&dwTemp) + 3) ) /*!< uint32_t 数据拆分 byte3 */void ANO_DT_Send_F1(uint16_t data1, uint16_t data2, uint16_t data3, uint16_t data4);
void ANO_DT_Send_F2(int16_t data1, int16_t data2, int16_t data3, int16_t data4);
void ANO_DT_Send_F3(int16_t data1, int16_t data2, int32_t data3);
#endif //STM32_ANO_UPPER_H

ano_upper.c

#include "ano_upper.h"
/** 发送数据缓存 */unsigned char data_to_send[50]; //用于绘图/*
* @brief 向上位机发送发送4个uint16_t数据
* @param data1: 发送给上位机显示波形 (可以自己加)
* @return 无
* @note 通过F1帧发送4个uint16类型数据
* @see ANO_DT_Send_F1
*/
void ANO_DT_Send_F1(uint16_t data1, uint16_t data2, uint16_t data3, uint16_t data4)
{unsigned char _cnt=0; //计数值unsigned char i = 0;unsigned char sumcheck = 0; //和校验unsigned char addcheck = 0; //附加和校验data_to_send[_cnt++] = 0xAA; //帧头 0xAAdata_to_send[_cnt++] = 0xFF; //目标地址data_to_send[_cnt++] = 0xF1; //功能码0xF1data_to_send[_cnt++] = 8; //数据长度8个字节//单片机为小端模式-低地址存放低位数据 匿名上位机要求先发低位数据, 所以先发低地址data_to_send[_cnt++]=BYTE0(data1);data_to_send[_cnt++]=BYTE1(data1);data_to_send[_cnt++]=BYTE0(data2);data_to_send[_cnt++]=BYTE1(data2);data_to_send[_cnt++]=BYTE0(data3);data_to_send[_cnt++]=BYTE1(data3);data_to_send[_cnt++]=BYTE0(data4);data_to_send[_cnt++]=BYTE1(data4);for(i=0; i < (data_to_send[3]+4); i++) //数据校验{sumcheck += data_to_send[i]; //从帧头开始,对每一字节进行求和,直到DATA区结束addcheck += sumcheck; //每一字节的求和操作,进行一次sumcheck的累加};data_to_send[_cnt++]=sumcheck;data_to_send[_cnt++]=addcheck;HAL_UART_Transmit(&huart1, data_to_send,_cnt,0xFFFF);
}
/*
* @brief 向上位机发送发送4个int16_t数据
* @param data1: 发送给上位机显示波形 (可以自己加)
* @return 无
* @note 通过F2帧发送4个int16类型数据
* @see ANO_DT_Send_F2
*/
void ANO_DT_Send_F2(int16_t data1, int16_t data2, int16_t data3, int16_t data4)
{unsigned char _cnt=0; //计数值unsigned char i = 0;unsigned char sumcheck = 0; //和校验unsigned char addcheck = 0; //附加和校验data_to_send[_cnt++] = 0xAA; //帧头 0xAAdata_to_send[_cnt++] = 0xFF; //目标地址data_to_send[_cnt++] = 0xF2; //功能码0xF2data_to_send[_cnt++] = 8; //数据长度8个字节//单片机为小端模式-低地址存放低位数据 匿名上位机要求先发低位数据, 所以先发低地址data_to_send[_cnt++]=BYTE0(data1);data_to_send[_cnt++]=BYTE1(data1);data_to_send[_cnt++]=BYTE0(data2);data_to_send[_cnt++]=BYTE1(data2);data_to_send[_cnt++]=BYTE0(data3);data_to_send[_cnt++]=BYTE1(data3);data_to_send[_cnt++]=BYTE0(data4);data_to_send[_cnt++]=BYTE1(data4);for(i=0; i < (data_to_send[3]+4); i++) //数据校验{sumcheck += data_to_send[i]; //从帧头开始,对每一字节进行求和,直到DATA区结束addcheck += sumcheck; //每一字节的求和操作,进行一次sumcheck的累加};data_to_send[_cnt++]=sumcheck;data_to_send[_cnt++]=addcheck;HAL_UART_Transmit(&huart1, data_to_send,_cnt,0xFFFF);
}
/*
* @brief 向上位机发送发送2个int16_t和1个int32_t数据
* @param data1: 发送给上位机显示波形 (可以自己加)
* @return 无
* @note 通过F3帧发送2个int16_t和1个int32_t数据
* @see ANO_DT_Send_F3
*/
void ANO_DT_Send_F3(int16_t data1, int16_t data2, int32_t data3)
{unsigned char _cnt=0; //计数值unsigned char i = 0;unsigned char sumcheck = 0; //和校验unsigned char addcheck = 0; //附加和校验data_to_send[_cnt++] = 0xAA; //帧头 0xAAdata_to_send[_cnt++] = 0xFF; //目标地址data_to_send[_cnt++] = 0xF3; //功能码0xF2data_to_send[_cnt++] = 8; //数据长度8个字节//单片机为小端模式-低地址存放低位数据 匿名上位机要求先发低位数据, 所以先发低地址data_to_send[_cnt++]=BYTE0(data1);data_to_send[_cnt++]=BYTE1(data1);data_to_send[_cnt++]=BYTE0(data2);data_to_send[_cnt++]=BYTE1(data2);data_to_send[_cnt++]=BYTE0(data3);data_to_send[_cnt++]=BYTE1(data3);data_to_send[_cnt++]=BYTE2(data3);for(i=0; i < (data_to_send[3]+4); i++) //数据校验{sumcheck += data_to_send[i]; //从帧头开始,对每一字节进行求和,直到DATA区结束addcheck += sumcheck; //每一字节的求和操作,进行一次sumcheck的累加};data_to_send[_cnt++]=sumcheck;data_to_send[_cnt++]=addcheck;HAL_UART_Transmit(&huart1, data_to_send,_cnt,0xFFFF);
}

main.c

//使用F2帧模式发送4个int16类型数据
ANO_DT_Send_F2(motor1_speed*100, motor2_speed*100, 1.0*100, 1.0*100);

显示
目标值为1.0
pid最终

image-20230824200902554

相关文章:

STM32使用PID调速

STM32使用PID调速 PID原理 PID算法是一种闭环控制系统中常用的算法&#xff0c;它结合了比例&#xff08;P&#xff09;、积分&#xff08;I&#xff09;和微分&#xff08;D&#xff09;三个环节&#xff0c;以实现对系统的控制。它的目的是使 控制系统的输出值尽可能接近预…...

【UE5:CesiumForUnreal】——3DTiles数据属性查询和单体高亮

目录 0.1 效果展示 0.2 实现步骤 1 数据准备 2 属性查询 2.1 射线检测 2.2 获取FeatureID 2.3 属性查询 2.4 属性显示 3 单体高亮 3.1 构建材质参数集 3.2 材质参数设置 3.3 添加Cesium Encode Metadata插件 3.4 从纹理中取出特定FeatureId属性信息 3.5 创建…...

无涯教程-PHP - 返回类型声明

在PHP 7中&#xff0c;引入了一个新函数返回类型声明&#xff0c;返回类型声明指定函数应返回的值的类型&#xff0c;可以声明返回类型的以下类型。 intfloatbooleanstringinterfacesarraycallable 有效返回类型 <?phpdeclare(strict_types1);function returnIntValue(i…...

DOS常见命令

DOS常见命令 DOS是什么如何打开DOScmd常见的命令集合 DOS是什么 DOC命令是我们浏览器中的终端 &#xff0c;但不同的是我们打开软件的方式 使用的是点击文件图标&#xff0c;点击图标的同时 我们也相当于使用一个命令 只是我们看不见而已 在电脑上操作的时候 通常都是使用命令…...

Qt应用开发(拓展篇)——示波器/图表 QCustomPlot

一、介绍 QCustomPlot是一个用于绘图和数据可视化的Qt C小部件。它没有进一步的依赖关系&#xff0c;提供友好的文档帮助。这个绘图库专注于制作好看的&#xff0c;出版质量的2D绘图&#xff0c;图形和图表&#xff0c;以及为实时可视化应用程序提供高性能。 QCustomPl…...

【精度丢失】后端接口返回的Long类型参数,不同浏览器解析出的结果不一样

1、业务背景 有个同事找我帮他看一个问题&#xff0c;他给前端提供了一个接口。 这个接口是用来反查id的&#xff0c;他这里这个参数正常的返回值应该是 283232039247028226。 但前端反馈他&#xff0c;前端在浏览器&#xff08;火狐&#xff09;获取的值是 283232039247028…...

2023年国赛 高教社杯数学建模思路 - 案例:感知机原理剖析及实现

文章目录 1 感知机的直观理解2 感知机的数学角度3 代码实现 4 建模资料 # 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 感知机的直观理解 感知机应该属于机器学习算法中最简单的一种算法&#xff0c;其…...

java-红黑树

节点内部存储 红黑树规则 或者&#xff1a; 红黑树添加节点规则&#xff1a; 添加节点默认是红色的&#xff08;效率高&#xff09; 红黑树示例 注&#xff1a;红黑树增删改查性能都很好...

vue2 vue中的常用指令

一、为什么要学习Vue 1.前端必备技能 2.岗位多&#xff0c;绝大互联网公司都在使用Vue 3.提高开发效率 4.高薪必备技能&#xff08;Vue2Vue3&#xff09; 二、什么是Vue 概念&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套 **构建用户界面 ** 的 渐进式 …...

AI驱动下的智能制造:工业自动化的新纪元

随着人工智能&#xff08;AI&#xff09;技术的持续进步&#xff0c;其在工业自动化领域的影响日益显著。作为现代科技的代表&#xff0c;AI不仅为各行业带来了前所未有的商机和技术思路&#xff0c;更在工业自动化领域中引发了一场深刻的变革。本文将深入探讨AI对智能制造的影…...

docker 命令

一、docker命令 1、镜像保存 docker save imageid -o modelzoozl.tar #把镜像保存到本地 docker load -i dockername #把tar包load下来&#xff0c;load成镜像 docker export CONTAINERID/CONTAINERNAME -o modelzoozl.tar #把启动着的镜像导出 docker import modelzo…...

2023年高教社杯数学建模思路 - 复盘:光照强度计算的优化模型

文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米&#xff0c;宽为12米&…...

生成式人工智能的潜在有害影响与未来之路(二)

利润高于隐私&#xff1a;不透明数据收集增加 背景和风险 生成型人工智能工具建立在各种大型、复杂的机器学习模型之上&#xff0c;这些模型需要大量的训练数据才能发挥作用。对于像ChatGPT这样的工具&#xff0c;数据包括从互联网上抓取的文本。对于像Lensa或Stable Diffusi…...

如何自己实现一个丝滑的流程图绘制工具(三)自定义挂载vue组件

背景 bpmn-js是个流程图绘制的工具&#xff0c;但是现在我希望实现的是&#xff0c;绘制的不是节点而是一个vue组件。 保留线的拖拽和连接。 方案 那就说明不是依赖于节点的样式&#xff0c;找到了他有个属性&#xff0c;就是类似覆盖节点的操作。 思路就是用vue组件做遮罩&…...

UNIAPP调用API接口

API&#xff1a;开发者可以通过这些接口与其它程序进行交互&#xff0c;获取所需数据或者执行指定操作。 网络请求 API: UniApp 中内置了网络请求 API&#xff0c;方便调用 uni.request uni.uploadFile uni.request 接口主要用于实现网络请求。GET 和 POST 是使用最普遍的两种…...

理解 Delphi 的类(五) - 认识类的继承

先新建一个 VCL Forms Application 工程, 代码中就已经出现了两个类: 一个是 TForm 类; 一个是 TForm1 类; TForm1 继承于 TForm. TForm 是 TForm1 的父类; TForm1 是 TForm 的子类. unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Contr…...

mybatis概述及搭建

目录 1.概述 2.mybatis搭建 1.创建一个maven项目&#xff0c;添加mybatis、mysql所依赖的jar 2.创建一个数据库表&#xff0c;及对应的java类 3.创建一个mybatis的核心配置文件&#xff0c;配置数据库连接信息&#xff0c;配置sql映射文件 4.创建sql映射文件&#xff0c;…...

DNDC模型---土壤碳储量、温室气体排放、农田减排、土地变化、气候变化中的应用

由于全球变暖、大气中温室气体浓度逐年增加等问题的出现&#xff0c;“双碳”行动特别是碳中和已经在世界范围形成广泛影响。国家领导人在多次重要会议上讲到&#xff0c;要把“双碳”纳入经济社会发展和生态文明建设整体布局。同时&#xff0c;提到要把减污降碳协同增效作为促…...

Android studio 2022.3.1 鼠标移动时不显示快速文档

在使用技术工具的过程中&#xff0c;我们时常会遇到各种各样的问题和挑战。最近&#xff0c;我升级了我的Android Studio到2022.3.1版本&#xff0c;但是在使用过程中&#xff0c;我碰到了一个让我颇为困扰的问题&#xff1a;在鼠标移动到类名或字段上时&#xff0c;原本应该显…...

五度易链最新“产业大数据服务解决方案”亮相,打造数据引擎,构建智慧产业!

快来五度易链官网 点击网址【http://www.wdsk.net/】 看看我们都发布了哪些新功能!!! 自2015年布局产业大数据服务行业以来&#xff0c;“五度易链”作为全国产业大数据服务行业先锋企业&#xff0c;以“让数据引领决策&#xff0c;以智慧驾驭未来”为愿景&#xff0c;肩负“打…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...