【快速上手ESP32(ESP-IDF)】ADC数模转换(含单次转换和连续转换以及校准)

这篇为重置版。

因为准备录制视频了,然后回过头看看之前讲ADC的文章发现有不少错误的地方(但是代码是可以用的),而且讲的也不全面,因此决定写下这个重置版。

这边提供三种使用ADC的方法,第一种是老方法,我就直接把之前的文章给复制过来并进行部分修正。看过的小伙伴可以跳过,直接看单次转换模式和连续转换模式。

目录

老方法

ADC单次转换模式

ADC连续转换模式

校准

老方法

首先是包含头文件。

#include "esp_adc_cal.h"
#include "driver/adc.h"

接下来进行两个配置。

第一个是配置adc1的精度,esp32中一共有俩adc,分别是adc1和adc2,按理说我们都可以用。但是我们最好就是用adc1,每个adc有十个通道,所以一个也是够用的。

至于为什么不用adc2,这是因为adc2和WiFi是冲突的,因此为了避免冲突,咱就是用adc1。

void adc1_config_width(adc_bits_width_t width);

可选的参数有9~13位精度的,但是根据之前的文档我们知道,ESP32中的ADC只有最高12位精度,因此13那个选项不能选(应该吧)。

下一个是衰减参数,衰减越大,可接受的电压越大。

void adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten);

第一个参数选择通道,我们知道每个ADC都有十个通道,因此可选的有十个。

第二个选择衰减参数。

这个根据自己手上的传感器的电气参数来选择,不清楚的话就先选11db的,这样就算电压范围不一样也不会导致板子烧坏。

接下来就可以读取ADC的值了。

int adc1_get_raw(adc1_channel_t channel);

另外需要说的是ADC的通道对应的GPIO口是固定的。

可以到官方文档里去查询。

GPIO & RTC GPIO - ESP32-S3 - — ESP-IDF 编程指南 v5.2.2 文档 (espressif.com)icon-default.png?t=N7T8https://docs.espressif.com/projects/esp-idf/zh_CN/v5.2.2/esp32s3/api-reference/peripherals/gpio.html

千万要记得左侧选择型号要选对。之前的文章就出现失误了,原因就是我没有选型号,结果GPIO和ADC的通道对不上。

完整实操代码如下。

#include <stdio.h>
#include "driver/gpio.h"
#include "driver/adc.h"
#include <unistd.h>void app_main(void){adc1_config_width(ADC_WIDTH_BIT_12); adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_DB_11);while (1){uint16_t adc_val = adc1_get_raw(ADC1_CHANNEL_0); printf("%d\r\n",adc_val);sleep(1);}
}

(上图是之前文章的图,配的文字也错了,这边就懒得改了。。。。。。)

ADC单次转换模式

我们跟着官方文档一步步走,步骤看着多,实际上只需要前三步即可。

首先还是需要包含头文件,这个可以在官方文档中ADC单次转化模式章节中的API参考小节找到,但是这俩头文件在小节中是分开来的,因此我之前没找到。

#include "hal/adc_types.h"
#include "esp_adc/adc_oneshot.h"

第一步资源分配,我们使用下面这个函数。

esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit)

第一个参数是结构体变量的指针,我们通过配置这个结构体来对ADC单元进行配置,第二个参数是传出参数,传出的是句柄。

我们来看看参数一结构体是怎么样的。

一共是三个成员,第一个是指定ADC,我们一般用的是ADC1,因此填入的参数是ADC_UNIT_1。第二个是时钟源,我们默认就行,选择ADC_RTC_CLK_SRC_DEFAULT。第三个是由ULP控制的模式,我们不给它控制,选择ADC_ULP_MODE_DISABLE

资源分配完之后我们进入下一步配置ADC单元实例。但其实我觉得这一步更像是配置ADC通道。

esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t channel, const adc_oneshot_chan_cfg_t *config)

参数一是上一步得到的ADC句柄,参数二指定通道,参数三是传入结构体变量的指针来配置。

通道可选的有下面十个,前面说的每个ADC都有十个通道。 

接着还是来看看参数三的结构体是什么样的。

一共俩成员,第一个是指定衰减,另一个是ADC转换结果的位数。

衰减可选的有下面几个,啥意思也都有注释。

但我估摸着看得懂的人不多(包括我),咱就参考下面这个,根据自己模块的电压范围选择,一般咱和MCU是连一起的,所以咱选最大的11dB衰减,起码烧不坏板子。

上面两个配置完之后我们就可以读取数据了。

esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)

参数一给个ADC句柄,参数二指定我们要读取的通道,参数三是传出参数,传出的是读取的结果。

接下来我用一个完整实例来演示一下。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"#include "esp_adc/adc_oneshot.h"
#include "hal/adc_types.h"adc_oneshot_unit_handle_t unit_handle;void ADC_init(void) {adc_oneshot_unit_init_cfg_t unit_initer = {.clk_src = ADC_RTC_CLK_SRC_DEFAULT,     // 默认时钟源.ulp_mode = ADC_ULP_MODE_DISABLE,       // 不启用ULP.unit_id = ADC_UNIT_1                   // 使用ADC1}; adc_oneshot_new_unit(&unit_initer, &unit_handle);adc_oneshot_chan_cfg_t channel_initer = {.atten = ADC_ATTEN_DB_11,               // 11dB衰减.bitwidth = ADC_BITWIDTH_12             // 输出12bit};adc_oneshot_config_channel(unit_handle, ADC_CHANNEL_0, &channel_initer);adc_oneshot_config_channel(unit_handle, ADC_CHANNEL_1, &channel_initer);
}void app_main(void) {ADC_init();int xVal = 0, yVal = 0;while (1) {adc_oneshot_read(unit_handle, ADC_CHANNEL_0, &xVal);adc_oneshot_read(unit_handle, ADC_CHANNEL_1, &yVal);printf("xval is %d yval is %d\r\n", xVal, yVal);vTaskDelay(1000 / portTICK_PERIOD_MS);}
}

我这边用的是双轴摇杆模块,摇杆的x轴和y轴的改变实际上是电位器,这边测试过后是可以读出来的。

这边有些小问题。

第一个是我每次读取都需要手动去调用读取函数。

第二个是我每个通道都需要单独去读取一次,这样太麻烦了。

那么接下来就轮到我们的ADC连续转换模式登场了。

ADC连续转换模式

先包含头文件。

#include "hal/adc_types.h"
#include "esp_adc/adc_continuous.h"

跟着步骤走,一共是五步。

第一步资源分配。

esp_err_t adc_continuous_new_handle(const adc_continuous_handle_cfg_t *hdl_config, adc_continuous_handle_t *ret_handle)

和单次转换差不多,参数一是用来配置的结构体,参数二是传出参数,传出的是句柄。

参数一的结构体就俩成员。

第一个是最大的缓冲字节数,参数二是转换帧的大小。

我们先来解释一下什么叫转换帧,转换帧就是将我们配置的ADC通道的转换结果连接起来合成一帧,就是下面这样。

每个通道的转换结果都会在转换帧中占用4个byte,那怕我们输出的大小就是12bit,它也是占用4个byte,因此我们参数二填入(需要配置的通道数*4)。

因为我们是连续转换模式,因此ADC会不停地转换,给我们不停地生成转换帧,那么参数一就是我们存放转换帧的缓冲区的大小,这个无所谓,随便给个值就行,但是要大于一个转换帧的大小。

第二步配置ADC,这一步稍微复杂一点。

esp_err_t adc_continuous_config(adc_continuous_handle_t handle, const adc_continuous_config_t *config)

参数一是上一步得到的句柄,参数二是配置用的结构体指针。

结构体的第一个成员是使用的ADC通道数,我们用几个就填几。第二个先空着,等等来。

成员三是ADC的采样频率,咱不懂填啥的话就参考官方的demo,官方填的是20 * 1000。

成员四是转换模式。因为我们只用了ADC1,所以选择ADC_CONV_SINGLE_UNIT_1

成员五是转换输出格式。其实我(ESP32S3)没得选,只能选类型二,ADC_DIGI_OUTPUT_FORMAT_TYPE2,如果是ESP32或者是ESP32S2的话就选类型一。

一开始试的时候我默认就是选择类型一,结果一直板子重启,然后我翻看日志才发现不让用类型一。看了官方demo才发现不同型号用的类型不一样。

接着回头看看成员二,需要传入的是结构体数组,数组里每个元素都配置一个ADC通道。

结构体如下。

其实成员我们都不陌生了,按照顺序就是衰减,通道,ADC资源,输出bit数,我们参考着单次转换模式那样填就行。

也可以参考一下我下面的写法。

    adc_digi_pattern_config_t adc_digi_arr[] = {{.atten = ADC_ATTEN_DB_11,.bit_width = ADC_BITWIDTH_12,.channel = ADC_CHANNEL_0,.unit = ADC_UNIT_1},{.atten = ADC_ATTEN_DB_11,.bit_width = ADC_BITWIDTH_12,.channel = ADC_CHANNEL_1,.unit = ADC_UNIT_1}};adc_continuous_config_t conti_config = {.adc_pattern = adc_digi_arr,                // 配置的通道数组.conv_mode = ADC_CONV_SINGLE_UNIT_1,        // 只使用ADC1.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,     // 没得选,只能选type2.pattern_num = 2,                           // 使用的通道数.sample_freq_hz = 20000                     // 采样频率,官方demo用的20 * 1000};adc_continuous_config(conti_handle, &conti_config);

第三步是ADC控制。

开启连续转换和停止连续转换用的是下面俩函数。

esp_err_t adc_continuous_start(adc_continuous_handle_t handle)

esp_err_t adc_continuous_stop(adc_continuous_handle_t handle)

虽然在官方文档里这是第三步,但是我们需要在第四步完成之后再调用,也就是注册完回调函数之后再开启转换。

我们看看第四步,注册回调函数。

esp_err_t adc_continuous_register_event_callbacks(adc_continuous_handle_t handle, const adc_continuous_evt_cbs_t *cbs, void *user_data)

参数一传入句柄,参数二给个结构体指针来配置回调函数,参数三是传给回调函数的参数,我们可以直接给个NULL。

来看看参数二的结构体类型。

一共是俩,第一个是转换完毕之后调用的回调函数,第二个是缓冲区满了之后调用的回调函数。

我们接着看看回调函数是有什么要求。

首先是返回值为bool类型,参数依次是句柄,事件给的参数,我们给的参数。

我们再接着看看事件给的参数是什么样的。

第一个是指向存放转换帧的缓冲区的指针,第二个是转换帧的大小。

也就是说我们可以通过回调函数的参数来获取到ADC转换的结果(多个转换帧)。

然后需要记住的是每个转换帧是 通道数*4,而我们只需要获取我们设置的输出bit即可,所以在取数据那一步需要额外注意一下。

接下来我贴一下完整的代码,可以参考着使用。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"#include "hal/adc_types.h"
#include "esp_adc/adc_continuous.h"adc_continuous_handle_t conti_handle;int adc_num;                    // 缓冲区大小
int xVal = 0, yVal = 0;          
uint8_t* adc_val;               // 缓冲区
bool adc_callback(adc_continuous_handle_t handle,const adc_continuous_evt_data_t* edata,void* user_data) {adc_num = edata->size;                                          // 获取缓冲区的大小adc_val = edata->conv_frame_buffer;                             // 获取转换结果if (adc_num == 8) {                                             // 将转换结果(4个byte)合成一个intxVal = (((uint16_t)adc_val[1] & 0x0F) << 8) | adc_val[0];   //因为我们设置的是输出12bit,因此需要把16bit的高4位去掉,所以需要&0x0FyVal = (((uint16_t)adc_val[5] & 0x0F) << 8) | adc_val[4];return true;}return false;
}void nADC_init(void) {adc_continuous_handle_cfg_t conti_initer = {.conv_frame_size = 8,               // 2 * 4 两个通道,每个4byte.max_store_buf_size = 1024          // 随便填,比2*4大就行};adc_continuous_new_handle(&conti_initer, &conti_handle);adc_digi_pattern_config_t adc_digi_arr[] = {{.atten = ADC_ATTEN_DB_11,       // 11dB衰减.bit_width = ADC_BITWIDTH_12,   // 输出12bit.channel = ADC_CHANNEL_0,       // 通道0.unit = ADC_UNIT_1              // ADC1},{.atten = ADC_ATTEN_DB_11,   .bit_width = ADC_BITWIDTH_12,.channel = ADC_CHANNEL_1,       // 通道1.unit = ADC_UNIT_1              }};adc_continuous_config_t conti_config = {.adc_pattern = adc_digi_arr,                // 配置的通道数组.conv_mode = ADC_CONV_SINGLE_UNIT_1,        // 只使用ADC1.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,     // 没得选,只能选type2.pattern_num = 2,                           // 使用的通道数.sample_freq_hz = 20000                     // 采样频率,官方demo用的20 * 1000};adc_continuous_config(conti_handle, &conti_config);adc_continuous_evt_cbs_t conti_evt = {.on_conv_done = adc_callback,               // 绑定转换完毕后的回调函数};adc_continuous_register_event_callbacks(conti_handle,&conti_evt,NULL);adc_continuous_start(conti_handle);             // 开启连续转换
}void app_main(void) {nADC_init();while (1) {for(int i = 0; i < adc_num; ++i){                   //打印看看缓冲区printf("%d\t",adc_val[i]);}printf("xval is %d yval is %d\r\n", xVal, yVal);vTaskDelay(1000 / portTICK_PERIOD_MS);}
}

除了上面这种直接通过回调函数来获取转换结果的方法,还有一个函数可以读取转换结果。

esp_err_t adc_continuous_read(adc_continuous_handle_t handle, uint8_t *buf, uint32_t length_max, uint32_t *out_length, uint32_t timeout_ms)

参数一是句柄。参数二是传出参数,也就是存放转换帧的缓冲区。参数三是我们要读取的长度,这边需要填通道数*4,我之前给的1024,结果它真的给我1024个数了。参数四是传出参数,给的是实际上读取的长度。参数五是等待时间。

这个函数我试过,就是它只有第一次读取的结果是准的,后面读出的结果是第一次结果的小幅度震荡,也就是说除了第一次是准的,后面读出的都是不准的。具体为什么我也不知道。

看了好久官方demo也找不出原因,所以就不贴出示例代码了,回调函数获取转换结果的方法能用,那就先用着。

校准

校准简单,创建完校准方案之后就可以直接用了。

先包含头文件。

#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"

第一步创建校准方案。

esp_err_t adc_cali_create_scheme_curve_fitting(const adc_cali_curve_fitting_config_t *config, adc_cali_handle_t *ret_handle)

参数一传入配置用的结构体指针,参数二传出参数,传输句柄。

结构体成员的名字和上面的不一样,但是类型是一样的,估计写代码的不是同一批人。

结构体成员依次是ADC资源,通道,衰减,输出bit数。

获得了句柄之后我们就可以通过调用下面这个函数来获取校准后的电压了,单位是mV。

esp_err_t adc_cali_raw_to_voltage(adc_cali_handle_t handle, int raw, int *voltage)

就俩步很简单。接下来直接贴上我的示例代码。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"#include "hal/adc_types.h"
#include "esp_adc/adc_continuous.h"#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"adc_cali_handle_t cali_handle;adc_continuous_handle_t conti_handle;int adc_num;                    // 缓冲区大小
int xVal = 0, yVal = 0;          
uint8_t* adc_val;               // 缓冲区
bool adc_callback(adc_continuous_handle_t handle,const adc_continuous_evt_data_t* edata,void* user_data) {adc_num = edata->size;                                          // 获取缓冲区的大小adc_val = edata->conv_frame_buffer;                             // 获取转换结果if (adc_num == 8) {                                             // 将转换结果(4个byte)合成一个intxVal = (((uint16_t)adc_val[1] & 0x0F) << 8) | adc_val[0];   //因为我们设置的是输出12bit,因此需要把16bit的高4位去掉,所以需要&0x0FyVal = (((uint16_t)adc_val[5] & 0x0F) << 8) | adc_val[4];return true;}return false;
}void nADC_init(void) {adc_continuous_handle_cfg_t conti_initer = {.conv_frame_size = 8,               // 2 * 4 两个通道,每个4byte.max_store_buf_size = 1024          // 随便填,比2*4大就行};adc_continuous_new_handle(&conti_initer, &conti_handle);adc_digi_pattern_config_t adc_digi_arr[] = {{.atten = ADC_ATTEN_DB_11,       // 11dB衰减.bit_width = ADC_BITWIDTH_12,   // 输出12bit.channel = ADC_CHANNEL_0,       // 通道0.unit = ADC_UNIT_1              // ADC1},{.atten = ADC_ATTEN_DB_11,   .bit_width = ADC_BITWIDTH_12,.channel = ADC_CHANNEL_1,       // 通道1.unit = ADC_UNIT_1              }};adc_continuous_config_t conti_config = {.adc_pattern = adc_digi_arr,                // 配置的通道数组.conv_mode = ADC_CONV_SINGLE_UNIT_1,        // 只使用ADC1.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,     // 没得选,只能选type2.pattern_num = 2,                           // 使用的通道数.sample_freq_hz = 20000                     // 采样频率,官方demo用的20 * 1000};adc_continuous_config(conti_handle, &conti_config);adc_continuous_evt_cbs_t conti_evt = {.on_conv_done = adc_callback,               // 绑定转换完毕后的回调函数};adc_continuous_register_event_callbacks(conti_handle,&conti_evt,NULL);adc_continuous_start(conti_handle);             // 开启连续转换
}void adc_cali_init(void){adc_cali_curve_fitting_config_t cali_initer = {.atten = ADC_ATTEN_DB_11,.bitwidth = ADC_BITWIDTH_12,.chan = ADC_CHANNEL_0,.unit_id = ADC_UNIT_1};adc_cali_create_scheme_curve_fitting(&cali_initer, &cali_handle);
}void app_main(void) {nADC_init();adc_cali_init();int newX,newY;while (1) {for(int i = 0; i < adc_num; ++i){                   //打印看看缓冲区printf("%d\t",adc_val[i]);}adc_cali_raw_to_voltage(cali_handle,xVal,&newX);adc_cali_raw_to_voltage(cali_handle,yVal,&newY);printf("xval is %d yval is %d newX is %dmV newY is %dmV\r\n", xVal, yVal,newX,newY);vTaskDelay(1000 / portTICK_PERIOD_MS);}
}

前半部分和连续转换模式的代码一样,直接看下半部分就行。

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

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

相关文章

港科夜闻 | 香港科大与阿里巴巴合作,计划成立大数据与人工智能联合实验室

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大与阿里巴巴合作&#xff0c;计划成立大数据与人工智能联合实验室。香港科大7月19日与阿里巴巴集团签署合作备忘录&#xff0c;计划成立「香港科技大学–阿里巴巴大数据与人工智能联合实验室」&#xff0c;就生成…

科普文:万字梳理31个Kafka问题

1、 kafka 是什么,有什么作用 2、Kafka为什么这么快 3、Kafka架构及名词解释 4、Kafka中的AR、ISR、OSR代表什么 5、HW、LEO代表什么 6、ISR收缩性 7、kafka follower如何与leader同步数据 8、Zookeeper 在 Kafka 中的作用&#xff08;早期&#xff09; 9、Kafka如何快…

UDP程序设计

UDP协议概述 UDP&#xff0c;User Datagram Protocol&#xff0c;用户数据报协议&#xff0c;是一个简单的面向数据报(package-oriented)的传输层协议&#xff0c;规范为&#xff1a;RFC 768。 UDP提供数据的不可靠传递&#xff0c;它一旦把应用程序发给网络层的数据发送出去…

Java | Leetcode Java题解之第300题最长递增子序列

题目&#xff1a; 题解&#xff1a; class Solution {public int lengthOfLIS(int[] nums) {int len 1, n nums.length;if (n 0) {return 0;}int[] d new int[n 1];d[len] nums[0];for (int i 1; i < n; i) {if (nums[i] > d[len]) {d[len] nums[i];} else {int…

做一个能和你互动玩耍的智能机器人之四--固件

在openbot的firmware目录下我们能够找到arduino的固件源码和相关的文档。 openbot的controller目录下&#xff0c;是控制器的代码目录&#xff0c;用来控制机器人做一些动作。未来的目标是加入大模型&#xff0c;使其能够理解人的语言和动作来控制。 固件代码&#xff0c;支持…

利用小爱同学与点灯科技+esp8266+舵机,制作智能关灯神器:小白也可制作,米家同步设备可实现多部手机进行控制。(亲测有用)

利用小爱同学与点灯科技&#xff0c;制作智能关灯神器&#xff1a;小白也可制作&#xff0c;米家同步设备可实现多部手机进行控制。 文章目录 利用小爱同学与点灯科技&#xff0c;制作智能关灯神器&#xff1a;小白也可制作&#xff0c;米家同步设备可实现多部手机进行控制。1.…

19. Revit API: Parameter(参数)

一、前言 我们在前面或多或少提到也用到参数了&#xff0c;这篇便细讲一下。 首先&#xff0c;我们知道好多信息都藏在参数里&#xff0c;或者说可以从参数中获取。我们还能够通过调整参数的值&#xff0c;改变模型的形态&#xff0c;即族的参变。 其次&#xff0c;有时族上…

一文讲透如何使用CSS美化HTML界面

上一篇文章(初识HTML文件&#xff0c;创建自己的第一个网页&#xff01;)介绍了HTML文件&#xff0c;并实战练习了如何编写一个结构简单的HTML网页&#xff0c;但最后也提到了单纯使用HTML语言创建的网页并不美观&#xff0c;有没有一个东西像PS能够美化图片一样让我们能够对我…

【前端 16】使用Ajax发送异步请求

Ajax 基础入门&#xff1a;实现异步请求 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;是一种在无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术。通过使用 Ajax&#xff0c;可以在后台与服务器交换数据&#xff0c;这意味着可以在不影响用户…

【代码】Python3|Scrapy框架初探(汽车之家大连市二手车车辆数据爬取、清洗与可视化)

本篇主要是整个项目的介绍&#xff0c;没提到太多琐碎的技术细节&#xff0c;以后有空的话会整理一下 Scrapy 和原生爬虫的差异&#xff0c;还有它坑人的一些地方&#xff0c;单发出来。 开源地址&#xff1a;https://github.com/shandianchengzi/car_home_spider 使用说明&a…

Github 2024-07-28 php开源项目日报Top10

根据Github Trendings的统计,今日(2024-07-28统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Blade项目2TypeScript项目2Java项目1ASP项目1Laravel:表达力和优雅的 Web 应用程序框架 创建周期:4631 天开发语言:PHP, BladeStar…

openssl 加密

使用tar命令在Linux中加密文件可以通过两种方式实现&#xff1a;使用gzip压缩的同时加密&#xff0c;或者使用加密选项。 1. 使用gzip压缩的同时加密&#xff1a; “ tar cz file1 file2 | openssl enc -e -aes256 -out archive.tar.gz.enc “ – cz&#xff1a;创建tar压缩文…

基于粒子群优化算法(PSO)永磁同步电机电流环多参数辨识MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 仿真模型简介 在同步旋转dq 轴坐标系下建立PMSM 数学模型&#xff0c;将定子dq 轴电压设为辨识模型和实际测量值的输入&#xff0c;设计了PSO 辨识PMSM 参数的适应度函数。该辨识方法不需推导复杂的电机数学…

R语言统计分析——整合和重构

参考资料&#xff1a;R语言实战【第2版】 R中提供了许多用来整合&#xff08;aggregate&#xff09;和重塑&#xff08;reshape&#xff09;数据的强大方法。在整合数据时&#xff0c;往往将多组观测替换为根据这些观测计算的描述性统计量。在重塑数据时&#xff0c;则会通过修…

STM32——GPIO(点亮LEDLED闪烁)

一、什么是GPIO&#xff1f; GPIO&#xff08;通用输入输出接口&#xff09;&#xff1a; 1.GPIO 功能概述 GPIO 是通用输入/输出&#xff08;General Purpose I/O&#xff09;的简称&#xff0c;既能当输入口使用&#xff0c;又能当输出口使用。端口&#xff0c;就是元器件…

数据结构(5.3_5)——二叉树的线索化

第一种寻找中序前驱方法 中序线索化 本质上就是一次中序遍历&#xff0c;只不过需要在一边遍历一边处理结点线索化 代码&#xff1a; //全局变量pre 指向当前访问结点的前驱 ThreadNode* pre NULL;struct ElemType {int value; };//线索二叉树结点 typedef struct ThreadNode…

linux练习2

一、搭建nfs服务器&#xff0c;客户端可从服务端/share目录上传与下载文件 **服务端** 1、下载相关安装包 [rootserver ~]# yum install rpcbind -y [rootserver ~]# yum install nfs-utils -y 2、 创建共享文件夹/share并授予权限 [rootserver ~]# mkdir /share [rootserv…

vector以及迭代器失效

前言 学习完string&#xff0c;之后学习的就是vector。vector就是之前C语言中讲到过的顺序表&#xff0c;用三个变量分别记录资源的位置&#xff0c;容器的容量和容器中元素个数。原来的写法是直接使用指针加两个int变量&#xff0c;而标准库中&#xff0c;三个都是由指针确定的…

sql server 连接报错error 40

做个简单的记录,造成40 的原因有很多,你的错误并不一定就是我遇到的这种情况. 错误描述: 首先我在使用ssms 工具连接的时候是可以正常连接的,也能对数据库进行操作. 在使用 ef core 连接 Sql Server 时报错: Microsoft.Data.SqlClient.SqlException (0x80131904): A network-r…

hadoop学习(二)

一.MapReduce 1.1定义&#xff1a;是一个分布式运算程序的编程框架 1.2核心功能&#xff1a;将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序&#xff0c;并发运行在一个Hadoop集群上。 1.3优点 1&#xff09;易于编程 它简单的实现一些接口&#…