AS5600编码器的使用
AS5600为绝对编码器,即根据安装的磁铁角度不同,初始位置的读数不同,读出来的数是当前初始位置在0~360°的一个值。(复现记录自用)
流程图:
初始化:
校准标定磁块【checkMagnetPresence()函数,通过返回值确定磁块位置是否准确距离】→读取初始角度值【ReadRawAngle()读取当前的绝对位置角度值信息】→
进入循环(每次循环都是有耗时,故转速不能超过某特定的值):
读取更新当前的角度值【ReadRawAngle()函数】→计算转过的角度数值【correctAngle()函数,可以计算角度值】→计算、累计转过的圈数【checkQuadrant()函数,通每次读取的象限,来确定是否转过一周】→更新显示屏的数值信息【refreshDisplay()函数】
1、该工程使用的是IIC总线通信,显示屏和编码器连接同一接口。
2、代码一
// 编码器测量出旋转角度值,并在显示屏中显示出来
// AS5600编码器
#include <Wire.h> //This is for i2C
#include <SSD1306Ascii.h> //i2C OLED库
#include <SSD1306AsciiWire.h> //i2C OLED库,GitHub开源可下载// i2C OLED
#define I2C_ADDRESS 0x3C
#define RST_PIN -1
SSD1306AsciiWire oled;
float OLEDTimer = 0; //屏幕刷新时间
//I2C pins:
//STM32: SDA: PB7 SCL: PB6
//Arduino: SDA: A4 SCL: A5//---------------------------------------------------------------------------
//Magnetic sensor things
int magnetStatus = 0; //磁块3种状态 (MD, ML, MH)int lowbyte; //raw angle 7:0
word highbyte; //raw angle 7:0 and 11:8
int rawAngle; //final raw angle
float degAngle; //raw angle in degrees (360/4096 * [value between 0-4095])int quadrantNumber, previousquadrantNumber; //quadrant IDs
float numberofTurns = 0; //number of turns
float correctedAngle = 0; //tared angle - based on the startup value
float startAngle = 0; //starting angle
float totalAngle = 0; //total absolute angular displacement
float previoustotalAngle = 0; //for the display printingvoid setup()
{Serial.begin(115200); //start serial - tip: don't use serial if you don't need it (speed considerations)Wire.begin(); //start i2C Wire.setClock(800000L); //fast clockcheckMagnetPresence(); //check the magnet (blocks until magnet is found)校准磁极ReadRawAngle(); //make a reading so the degAngle gets updatedstartAngle = degAngle; //update startAngle with degAngle - for taring//------------------------------------------------------------------------------//OLED 部分#if RST_PIN >= 0oled.begin(&Adafruit128x32, I2C_ADDRESS, RST_PIN);#else // RST_PIN >= 0oled.begin(&Adafruit128x32, I2C_ADDRESS);#endif // RST_PIN >= 0oled.setFont(Adafruit5x7);oled.clear(); //clear displayoled.set2X(); //double-line font size - better to read itoled.println("Welcome!"); //print a welcome message oled.println("AS5600"); //print a welcome messagedelay(3000);OLEDTimer = millis(); //start the timer}void loop()
{ ReadRawAngle(); //ask the value from the sensorcorrectAngle(); //tare the valuecheckQuadrant(); //check quadrant, check rotations, check absolute angular positionrefreshDisplay();//delay(100); //wait a little - adjust it for "better resolution"}// 读取编码器的绝对位置的数值
void ReadRawAngle()
{ //7:0 - bitsWire.beginTransmission(0x36); //connect to the sensorWire.write(0x0D); //figure 21 - register map: Raw angle (7:0)Wire.endTransmission(); //end transmissionWire.requestFrom(0x36, 1); //request from the sensorwhile(Wire.available() == 0); //wait until it becomes available lowbyte = Wire.read(); //Reading the data after the request//11:8 - 4 bitsWire.beginTransmission(0x36);Wire.write(0x0C); //figure 21 - register map: Raw angle (11:8)Wire.endTransmission();Wire.requestFrom(0x36, 1);while(Wire.available() == 0); highbyte = Wire.read();//4 bits have to be shifted to its proper place as we want to build a 12-bit numberhighbyte = highbyte << 8; //shifting to left//What is happening here is the following: The variable is being shifted by 8 bits to the left://Initial value: 00000000|00001111 (word = 16 bits or 2 bytes)//Left shifting by eight bits: 00001111|00000000 so, the high byte is filled in//Finally, we combine (bitwise OR) the two numbers://High: 00001111|00000000//Low: 00000000|00001111// -----------------//H|L: 00001111|00001111rawAngle = highbyte | lowbyte; //int is 16 bits (as well as the word)//We need to calculate the angle://12 bit -> 4096 different levels: 360° is divided into 4096 equal parts://360/4096 = 0.087890625//Multiply the output of the encoder with 0.087890625degAngle = rawAngle * 0.087890625; Serial.println(degAngle);//Serial.print("Deg angle: ");//Serial.println(degAngle, 2); //absolute position of the encoder within the 0-360 circle}// 校对编码器的数值,因为编码器是0~360°的位置绝对值,需要计算其的圈数进行累加
void correctAngle()
{//recalculate anglecorrectedAngle = degAngle - startAngle; //this tares the position 没变动则为0;变动的数值if(correctedAngle < 0) //if the calculated angle is negative, we need to "normalize" it{correctedAngle = correctedAngle + 360; //correction for negative numbers (i.e. -15 becomes +345)}else{//do nothing}//Serial.print("Corrected angle: ");//Serial.println(correctedAngle, 2); //print the corrected/tared angle
}// 用相数的变化来计算旋转的圈数,用于校对
void checkQuadrant()
{/*//Quadrants:4 | 1---|---3 | 2*///Quadrant 1if(correctedAngle >= 0 && correctedAngle <=90){quadrantNumber = 1;}//Quadrant 2if(correctedAngle > 90 && correctedAngle <=180){quadrantNumber = 2;}//Quadrant 3if(correctedAngle > 180 && correctedAngle <=270){quadrantNumber = 3;}//Quadrant 4if(correctedAngle > 270 && correctedAngle <360){quadrantNumber = 4;}//Serial.print("Quadrant: ");//Serial.println(quadrantNumber); //print our position "quadrant-wise"if(quadrantNumber != previousquadrantNumber) //if we changed quadrant{if(quadrantNumber == 1 && previousquadrantNumber == 4){numberofTurns++; // 4 --> 1 transition: CW rotation}if(quadrantNumber == 4 && previousquadrantNumber == 1){numberofTurns--; // 1 --> 4 transition: CCW rotation}//this could be done between every quadrants so one can count every 1/4th of transitionpreviousquadrantNumber = quadrantNumber; //update to the current quadrant} //Serial.print("Turns: ");//Serial.println(numberofTurns,0); //number of turns in absolute terms (can be negative which indicates CCW turns) //after we have the corrected angle and the turns, we can calculate the total absolute positiontotalAngle = (numberofTurns*360) + correctedAngle; //number of turns (+/-) plus the actual angle within the 0-360 range//Serial.print("Total angle: ");//Serial.println(totalAngle, 2); //absolute position of the motor expressed in degree angles, 2 digits
}// 初始时对MCU进行锁定,确定其位置进行标定
void checkMagnetPresence()
{ //This function runs in the setup() and it locks the MCU until the magnet is not positioned properly// 他的函数在setup()中运行,它锁定MCU,直到磁铁没有正确定位 while((magnetStatus & 32) != 32) //while the magnet is not adjusted to the proper distance - 32: MD = 1// 磁铁位置是否调整到合适的位置。{ magnetStatus = 0; //reset readingWire.beginTransmission(0x36); //connect to the sensorWire.write(0x0B); //figure 21 - register map: Status: MD ML MHWire.endTransmission(); //end transmissionWire.requestFrom(0x36, 1); //request from the sensorwhile(Wire.available() == 0); //wait until it becomes available magnetStatus = Wire.read(); //Reading the data after the request//Serial.print("Magnet status: ");//Serial.println(magnetStatus, BIN); //print it in binary so you can compare it to the table (fig 21) } //Status register output: 0 0 MD ML MH 0 0 0 //MH: Too strong magnet - 100111 - DEC: 39 //ML: Too weak magnet - 10111 - DEC: 23 //MD: OK magnet - 110111 - DEC: 55//Serial.println("Magnet found!");//delay(1000);
}void refreshDisplay()
{if (millis() - OLEDTimer > 100) //chech if we will update at every 100 ms{ if(totalAngle != previoustotalAngle) //if there's a change in the position*{oled.clear(); //delete the content of the displayoled.println(totalAngle); //print the new absolute positionOLEDTimer = millis(); //reset timer previoustotalAngle = totalAngle; //update the previous value}}else{//skip}//*idea: you can define a certain tolerance for the angle so the screen will not flicker//when there is a 0.08 change in the angle (sometimes the sensor reads uncertain values)
}
二、AS5600利用数码轮同步转动
1、解释
旋转脉冲编码器,步进电机同步旋转到对应的位置,而编码器起到测量反馈的作用。
2、代码二
#include <Wire.h> //This is for i2C
#include <SSD1306Ascii.h> //i2C OLED
#include <SSD1306AsciiWire.h> //i2C OLED// i2C OLED
#define I2C_ADDRESS 0x3C
#define RST_PIN -1
SSD1306AsciiWire oled;
float OLEDTimer = 0; //Timer for the screen refresh
//I2C pins:
//STM32: SDA: PB7 SCL: PB6
//Arduino: SDA: A4 SCL: A5//---------------------------------------------------------------------------
//Magnetic sensor things
int magnetStatus = 0; //value of the status register (MD, ML, MH)int lowbyte; //raw angle 7:0
word highbyte; //raw angle 7:0 and 11:8
int rawAngle; //final raw angle
float degAngle; //raw angle in degrees (360/4096 * [value between 0-4095])int quadrantNumber, previousquadrantNumber; //quadrant IDs
float numberofTurns = 0; //number of turns
float correctedAngle = 0; //tared angle - based on the startup value
float startAngle = 0; //starting angle
float totalAngle = 0; //total absolute angular displacement
float previoustotalAngle = 0; //for the display printing
float encoderTimer = 0;
//---------------------------------------------------------------------------
int pinA = PB10; // Pin A of the encoder
int pinB = PB11; // Pin B of the encoder//CNC Decoder behavior
// CNC编译器特性
//CW rotation: output of B is half square wave delayed from output of A
// CW旋转模式下:B输出比A的输出延迟半个方波
//CCW rotation: output of A is half square wave delayed from output of B
// CCW旋转模式下:A输出比B的输出延迟半个方波//The pulse generator's output can be DIRECTLY wired to the step and dir pins.
//脉冲发生器的输出可以直接连接到step和dir引脚。
//This means that the microcontroller can be omitted!!! - of course there won't be any feedback then
//这意味着可以省略微控制器!! 当然那时不会有任何反馈 volatile int numberofclicks = 0; //Stores the number of click done by the encoder. 1 turn = 100 clicks// 存储编码器完成的点击次数。 1回合= 100次点击
int previous_numberofclicks = 0; //Stores the "previous" number of clicks. Helps us to see if the encoder was moved// 存储“以前”的点击次数。 帮助我们确定编码器是否被移动过 //--Stepper motor related 步进电机相关----------------------------------------------------------
#include <AccelStepper.h>
AccelStepper stepper(1, PA9, PA8);// pulses/steps 9; Direction 8
const int stepperEnablePin = PB12; //enable/disable pin for the stepper motor driver
//remember that for Arduino, you don't need the "PA" and "PB" prefixes. Just use 1,2,3...etc.void setup()
{pinMode(pinA, INPUT_PULLUP); //A terminal of the CNC wheelpinMode(pinB, INPUT_PULLUP); //B terminal of the CNC wheelattachInterrupt(digitalPinToInterrupt(pinA), pinAInterrupt, RISING); //pin A is an interrupt 低电平变高电平触发,中断函数被触发pinAInterrupt()。Serial.begin(115200); //start serial - tip: don't use serial if you don't need it (speed considerations)Wire.begin(); //start i2C Wire.setClock(800000L); //fast clock//General remark on i2C: it seems that the i2C interferes with the attachInterrupt() in some way causing//strange readings if the i2C-related hardware is read too often (in every loop iteration). // 注意的bug: 似乎i2C以某种方式干扰了attachInterrupt()导致;// 如果i2c相关的硬件被读得太频繁(在每个循环迭代中),则会出现奇怪的读数。checkMagnetPresence(); //check the magnet (blocks until magnet is found)ReadRawAngle(); //make a reading so the degAngle gets updatedstartAngle = degAngle; //update startAngle with degAngle - for taring//------------------------------------------------------------------------------//OLED part#if RST_PIN >= 0oled.begin(&Adafruit128x32, I2C_ADDRESS, RST_PIN);#else // RST_PIN >= 0oled.begin(&Adafruit128x32, I2C_ADDRESS);#endif // RST_PIN >= 0oled.setFont(Adafruit5x7);oled.clear(); //clear displayoled.set2X(); //double-line font size - better to read it 建立两行oled.println("Welcome!"); //print a welcome message oled.println("AS5600"); //print a welcome message//Stepper setup---------------------------------------------------------stepper.setSpeed(1000); //SPEED = Steps / secondstepper.setMaxSpeed(1000); //SPEED = Steps / secondstepper.setAcceleration(5000); //ACCELERATION = Steps /(second)^2 pinMode(stepperEnablePin, OUTPUT); //enable/disable pin is defined as an outputdigitalWrite(stepperEnablePin, LOW); //enable motor current//disabling the current can prevent the driver and the motor running hot//on the other hand, it can lead to inaccuracies because the motor is not held at place when it is not under power delay(2000);OLEDTimer = millis(); //start the timerencoderTimer = millis(); //start encoder timer} void loop()
{ if(millis()- encoderTimer > 125) //125 ms will be able to make 8 readings in a sec which is enough for 60 RPM//60转每分钟{ ReadRawAngle(); //ask the value from the sensorcorrectAngle(); //tare the valuecheckQuadrant(); //check quadrant, check rotations, check absolute angular position encoderTimer = millis(); /*A little brainstorm on determining the required delay* The above 3 functions require about 300-310 us to finish* They mess up the interrupt of the CNC encoder due to the i2C communication* Therefore it is not good if they are called very often* We want to detect at least every rotations of the shaft* I say (arbitrarily), that we need to detect at least 2 angles in each quadrants, so in 1 turn of the shaft, there are 8 readings* 我说(随意地),我们需要在每个象限中检测至少两个角度,所以在轴的一圈中,有8个读数 * 8 readings per turn can be converted into readings per second based on the expected highest speed* 每轮8个读数可转换为基于预期的最高速度的每秒读数 * Example:* 60 RPM = 60/60 RPS (rounds per seconds) = 1 RPS* 1 round per second -> 8 reading per second -> 1 second/8 readings = 0.125 s = 125 ms is the frequency of readings* * Example 2:* * 100 RPM = 100/60 = 1.667 RPS* 1 round = 0.599 s -> 0.599 s/ 8 readings = 74.98 ~ 75 ms. * Check: 60/100 = 0.6 -> 75/125 = 0.6. */}refreshDisplay(); //refresh the display - won't refresh until certain conditions are not fulfilled // 刷新显示-在某些条件未满足之前不会刷新 while (stepper.distanceToGo() != 0) //This blocks the rest of the code!{stepper.runSpeedToPosition(); //Runs to the target position defined by the moveTo() function //does not use accelerations //运行到moveTo()函数定义的目标位置 ,不使用加速 }}void ReadRawAngle()
{ //7:0 - bitsWire.beginTransmission(0x36); //connect to the sensorWire.write(0x0D); //figure 21 - register map: Raw angle (7:0)Wire.endTransmission(); //end transmissionWire.requestFrom(0x36, 1); //request from the sensorwhile(Wire.available() == 0); //wait until it becomes available lowbyte = Wire.read(); //Reading the data after the request//11:8 - 4 bitsWire.beginTransmission(0x36);Wire.write(0x0C); //figure 21 - register map: Raw angle (11:8)Wire.endTransmission();Wire.requestFrom(0x36, 1);while(Wire.available() == 0); highbyte = Wire.read();//4 bits have to be shifted to its proper place as we want to build a 12-bit numberhighbyte = highbyte << 8; //shifting to left//What is happening here is the following: The variable is being shifted by 8 bits to the left://Initial value: 00000000|00001111 (word = 16 bits or 2 bytes)//Left shifting by eight bits: 00001111|00000000 so, the high byte is filled in//Finally, we combine (bitwise OR) the two numbers://High: 00001111|00000000//Low: 00000000|00001111// -----------------//H|L: 00001111|00001111rawAngle = highbyte | lowbyte; //int is 16 bits (as well as the word)//We need to calculate the angle://12 bit -> 4096 different levels: 360° is divided into 4096 equal parts://360/4096 = 0.087890625//Multiply the output of the encoder with 0.087890625degAngle = rawAngle * 0.087890625; //Serial.print("Deg angle: ");//Serial.println(degAngle, 2); //absolute position of the encoder within the 0-360 circle}void correctAngle()
{//recalculate anglecorrectedAngle = degAngle - startAngle; //this tares the positionif(correctedAngle < 0) //if the calculated angle is negative, we need to "normalize" it{correctedAngle = correctedAngle + 360; //correction for negative numbers (i.e. -15 becomes +345)}else{//do nothing}//Serial.print("Corrected angle: ");//Serial.println(correctedAngle, 2); //print the corrected/tared angle
}void checkQuadrant()
{/*//Quadrants:4 | 1---|---3 | 2*///Quadrant 1if(correctedAngle >= 0 && correctedAngle <=90){quadrantNumber = 1;}//Quadrant 2if(correctedAngle > 90 && correctedAngle <=180){quadrantNumber = 2;}//Quadrant 3if(correctedAngle > 180 && correctedAngle <=270){quadrantNumber = 3;}//Quadrant 4if(correctedAngle > 270 && correctedAngle <360){quadrantNumber = 4;}//Serial.print("Quadrant: ");//Serial.println(quadrantNumber); //print our position "quadrant-wise"if(quadrantNumber != previousquadrantNumber) //if we changed quadrant{if(quadrantNumber == 1 && previousquadrantNumber == 4){numberofTurns++; // 4 --> 1 transition: CW rotation}if(quadrantNumber == 4 && previousquadrantNumber == 1){numberofTurns--; // 1 --> 4 transition: CCW rotation}//this could be done between every quadrants so one can count every 1/4th of transitionpreviousquadrantNumber = quadrantNumber; //update to the current quadrant} //Serial.print("Turns: ");//Serial.println(numberofTurns,0); //number of turns in absolute terms (can be negative which indicates CCW turns) //after we have the corrected angle and the turns, we can calculate the total absolute positiontotalAngle = (numberofTurns*360) + correctedAngle; //number of turns (+/-) plus the actual angle within the 0-360 range//Serial.print("Total angle: ");//Serial.println(totalAngle, 2); //absolute position of the motor expressed in degree angles, 2 digits
}void checkMagnetPresence()
{ //This function runs in the setup() and it locks the MCU until the magnet is not positioned properlywhile((magnetStatus & 32) != 32) //while the magnet is not adjusted to the proper distance - 32: MD = 1{magnetStatus = 0; //reset readingWire.beginTransmission(0x36); //connect to the sensorWire.write(0x0B); //figure 21 - register map: Status: MD ML MHWire.endTransmission(); //end transmissionWire.requestFrom(0x36, 1); //request from the sensor while(Wire.available() == 0); //wait until it becomes available magnetStatus = Wire.read(); //Reading the data after the request//Serial.print("Magnet status: ");//Serial.println(magnetStatus, BIN); //print it in binary so you can compare it to the table (fig 21) } //Status register output: 0 0 MD ML MH 0 0 0 //MH: Too strong magnet - 100111 - DEC: 39 //ML: Too weak magnet - 10111 - DEC: 23 //MD: OK magnet - 110111 - DEC: 55//Serial.println("Magnet found!");delay(1000);
}void refreshDisplay()
{if (millis() - OLEDTimer > 250) //chech if we will update at every 100 ms 检查我们是否每100毫秒更新一次{ if(totalAngle != previoustotalAngle || previous_numberofclicks != numberofclicks) //if there's a change in the position*//if(previous_numberofclicks != numberofclicks) //if there's a change in the position*{oled.clear(); //delete the content of the displayoled.print("M: "); //M: Magnet signal (Degrees)电磁信号(度)oled.println(totalAngle); //print the new absolute positionoled.print("W: "); //W: Wheel signal (Clicks) (轮的信号:点击数oled.println(numberofclicks);OLEDTimer = millis(); //reset timer 重置时钟 previoustotalAngle = totalAngle; //update the previous valueprevious_numberofclicks = numberofclicks; //update current position}}else{//skip}//*idea: you can define a certain tolerance for the angle so the screen will not flicker//when there is a 0.08 change in the angle (sometimes the sensor reads uncertain values)// //*想法:你可以定义一定的角度公差,这样屏幕就不会闪烁 //当角度有0.08的变化时(有时传感器读取不确定值)
}void pinAInterrupt()
{//When pin A's wave is detected...//当检测到引脚A的波时…if (digitalRead(pinB) == 0) //if B is LOW, it means that pin A's wave occured first -> CW rotation occured (I had to change it because of the stepper motor)//如果B是低的,这意味着引脚A的波先发生-> CW旋转发生(我不得不改变它,因为步进电机) {numberofclicks++; //increase value //Serial.println(numberofclicks); //do not use delays or prints in the final code, use it only for debugging/developing}else //if B is HIGH, it means that pin B's wave occured first. So, when pin A has a rising edge, pin B is alreadi high -> CCW rotation//如果B为高,则表示B引脚的波先出现。 因此,当引脚A有上升边时,引脚B已经是高的-> CCW旋转 {numberofclicks--; //decrease value//Serial.println(numberofclicks);}//Serial.println(numberofclicks);stepper.moveTo(-1*numberofclicks); //Updates the "go to" position of the stepper motor - absolute value//The above moveTo() function means that if the numberofclick variable = 936, then the stepper motor will be//936 steps away from the origin. //更新步进电机的“go to”位置-绝对值 //上面的moveTo()函数的意思是,如果numberofclick变量= 936,则步进电机将 //距离原点936步。
AS5600编码器的使用相关推荐
- AS5600编码器使用讲解
AS5600编码器使用讲解 //³õʼ»¯IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphCl ...
- 步进电机正反转驱动、AS5600编码器信息读取及速度检测
步进电机基本运动 1.思路: 用定时器替代mstimer2库,mstimer2底层实现是延时函数控制,delay函数占用CPU资源,使得CPU在delay函数工作时无法处理其他任务,二是delay函数 ...
- INA240三相无刷电机电流采样实例(arduino)
目录 前言&&准备材料 arduino程序 参考链接: 前言&&准备材料 我这里用的控制器是esp32,它的adc采集器分辨率是12位,工作电压是3.3V.因此我们读取 ...
- 从零开始做手机云台/稳定器
最近完成手机云台/稳定器的产品化,特意记录整个手机云台的开发过程 (内容持续细化中...) 一. 电机驱动篇 硬件描述: 直流无刷电机,7对极,控制板mcu为stm32f0. 驱动方式: FOC 控制 ...
- Makerbase SimpleFOC ESP32 例程1 双电机开环速度测试
Makerbase SimpleFOC ESP32 例程1 双电机开环速度测试 第一部分 硬件介绍 1.1 硬件清单 序号 品名 数量 1 ESP32 FOC V1.0 主板 1 2 YT2804电机 ...
- SimpleFOC之ESP32(三)—— 闭环控制
目录 一.硬件介绍 1.1.原理图 1.2.ESP32drive方案 1.2.1.准备清单 1.2.2.硬件连接 1.3.SimpleFOCShield方案 1.3.1.准备清单 1.3.2.硬件连接 ...
- SimpleFOC移植STM32(五)—— 电流采样及其变换
目录 一.原理说明 1.1.电流采样 1.1.1.为什么要采样电流 1.1.2.电流采样方式 1.1.2.1.低侧电流采样 1.1.2.2.高侧电流采样 1.1.2.3.内置电流采样 1.2.电流变换 ...
- SimpleFOC之ESP32(六)—— 双电机控制
目录 说明 一.硬件介绍 1.1.原理图 1.2.ESP32drive-D方案 1.2.1.准备清单 1.2.2.硬件连接 1.3.SimpleFOCShield方案 1.3.1.SimpleFOCS ...
- TLE5012B磁编码器原理及优点,径向充磁磁铁的安装。AS5047、AS5048、AS5600、TLE5012、MA730
磁编码器以其独特的优点,在近两年的产品应用上十分流行.它体积小巧.安装方便.分辨率高.较光电编码器成本更低.不受灰尘油渍影响.可非接触安装.便于与电机集成一体实现伺服控制.相较于光电编码器,磁编码 ...
最新文章
- Winform中实现文件批量更名器(附代码下载)
- ios 图片 相册 存储方式
- android异步线程未执行,关于多线程:当服务在后台运行时,Android异步任务无法正常运行(doInBackground未执行)...
- js input 自动换行_深入Slate.js - 拯救 ContentEditble
- 小程序 pagescrollto_微信小程序学习笔记(三)-- 首页及详情页开发
- 刘慈欣推荐!这本“疯狂的书”预言9年后人类将永生?
- python多继承_Python多继承,__init__
- 2022 电工杯 B 物资配送 全部图解
- Java 一个简单的接口使用案例
- c语言标准库详解(九):实用函数stdlib.h
- 学习了pr后的收获_Pr实训报告心得体会
- 王宇阳:六个案例里的SEO启发
- 挖掘人工智能心理学新方向
- Android 中网络连接检测和使用ping检测网络是否可访问
- Word下划线怎么打?速速get这5个实用方法!
- 用python将多张图片拼接成一张
- 6年全栈工程师回答:web前端的主要学习什么,现在还有前途吗?一般工资是多少?
- Elixir服务器接收客户端消息01
- 湖北大学计算机考入清华,高考上湖北大学保送清华大学直博生,失聪左耳给了他一个安静世界...
- Yahoo 前端雅虎军规
热门文章
- Netty框架架构解析+API+运行流程+网络编程文章集锦
- 制作Linux嵌入式系统开机LOGO(图片)
- 【解决】装有Windows 10的笔记本电脑空闲时风扇突然转得很快,CPU与磁盘等占用率飙升的解决方法
- 为什么需要智能化,他和自动化相比有什么有利方面?
- SAP770系统FI模块配置(向一个会计年度变式分配公司代码)
- 【selenium】对浏览器的操作 浏览器的最大化,设置浏览器的宽和高,浏览器的前进和后退,浏览器滚动条的控制
- 斐波那契数列的迭代算法和递归算法
- 解决预览pdf不能下载的问题
- vr全景看房是怎么做的?vr全景看房平台有哪些?
- 解开IPA文件的灰沙-通过静态分析工具了解IPA实现