找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 369|回复: 0
打印 上一主题 下一主题
收起左侧

搬运个带金属鉴别功能的Arduino金属探测器帖子

[复制链接]
跳转到指定楼层
楼主
原来想做个金属检测器,一直没搞成,无意找到个这玩意,搬运过来给有兴趣的做个参考,有这个板子的和懂这个程序的可以验证下这个是不是真的可行,提供的那个。原文是英文看不懂干脆用翻译软件翻译成中文在后面,正文开始:
Arduino Metal Detector with Metal Discrimination


In this article, we will consider the creation of a metal detector based on the Arduino board with the ability to discriminate metals. This metal detector will be able to detect small metal objects (for example, coins) at a depth of up to 15 cm, and large metal objects it will be able to detect at a depth of up to 50 cm (and even more). It will also be able to distinguish iron-containing metals (ferrous) from non-ferrous metals (nonferrous). The metal detector has a fairly simple design and at the same time it provides acceptable sensitivity.


Earlier on our website we reviewed a project of a simple metal detector on Arduino , we recommend reading it to understand the principle of operation of a metal detector.
Necessary components
  • Arduino Nano board
  • LCD display 16×2
  • Operational amplifier TL081 or 741, LT1677 will also work (used by the author of the project)
  • Speaker 0.25 W, 8 Ohm.
  • General purpose transistor NPN type.
  • Induction coil – 2 pcs.
  • Potentiometer 10 kOhm
  • Capacitors and resistors (as per the diagram below).
  • Switches.
  • Battery.

Project scheme

The circuit diagram of the metal detector on Arduino with metal discrimination is shown in the following figure.


When metal is detected, the device will emit a sound signal, and the LCD display will display the proximity of the metal using a bar graph, as well as indicate the type of metal – iron (ferrous) or non-ferrous (non-ferrous).

The device is an induction-balanced metal detector operating at a very low frequency (VLF). The metal detector contains a transmitting and receiving inductance coil. As in all circuits of similar detectors, the balance between the coils is very important for our device. The potentiometer in the detector circuit is used to eliminate the influence of the out-of-phase component of the signal – it brings the phase shift to zero, and the in-phase component is zeroed using the appropriate arrangement of the coils – according to the operating principle of IB detectors.


Each induction coil is made by winding 64 turns of 0.5 mm2 enameled copper wire onto a D shape with a diameter of 11 cm. The coil structure is then wrapped with tape and shielded with aluminum foil, after which tinned copper wire is attached to it – it is necessary to leave a small gap in the foil to attach it. After that, both coils are fixed to a plastic base. The appearance of the assembled induction coils for the metal detector is shown in the following figure.


You can watch the assembly process in more detail in the video provided at the end of the article. The appearance of the assembled metal detector structure is shown in the following figure.


To set up the project, we first need to determine the resonant frequency of the oscillatory circuit in our circuit. To do this, you can use the formula known from the physics course, online calculators, or you can measure it with an oscilloscope. If you assembled the coils in the described way, then the resonant frequency of our circuit should be approximately 7.64 kHz. If you received a different resonant frequency value, then you need to make the appropriate changes to the next line of the program:


  1. #define TIMER1_TOP (249) // fine-tune the frequency
复制代码

As you can see in the video below, the results of the metal detector were quite impressive. In the absence of metal, the device shows stable operation. A metal circle with a diameter of 15 cm is detected at a distance of more than 30 cm. Larger metal objects are detected at distances exceeding 40-50 cm. A small coin can be detected at a distance of 15 cm (in the air).

To power the metal detector, the author of the project used two lithium batteries, which, when connected in series, provide a supply voltage of 7.4 V – this voltage is supplied to the Vin contact of the Arduino board. The power consumption of the device does not exceed 20 mA, so it will work for quite a long time from such batteries.

To significantly increase the sensitivity of the metal detector, the author of the project proposes to control the transmitting coil using a powerful MOSFET transistor – in the future, he plans to publish the results of such an experiment on the project page.



Note : recommendations for practical assembly of this metal detector project from an active reader of our site named Alexander with a description, photographs and video can be downloaded fromthe following link .


Source code of the program (sketch)

  1. // Induction balance metal detector


  2. // We run the CPU at 16MHz and the ADC clock at 1MHz (АЦП работает на 1 МГц). ADC resolution is reduced to 8 bits at this speed. (разрешение АЦП уменьшено до 8 бит)


  3. // Timer 1 is used to divide the system clock by about 256 to produce a 62.5kHz square wave. (Таймер 1 использовается для формирования прямоугольных импульсов с частотой 62,5 кГц)

  4. // This is used to drive timer 0 and also to trigger ADC conversions.

  5. // Timer 0 is used to divide the output of timer 1 by 8, giving a 7.8125kHz signal for driving the transmit coil.

  6. // Таймер 0 делит выход таймера 1 на 8, и, таким образом, формирует сигнал частотой 7.8125kHz для управления передающей катушкой

  7. // This gives us 16 ADC clock cycles for each ADC conversion (it actually takes 13.5 cycles), and we take 8 samples per cycle of the coil drive voltage.

  8. // The ADC implements four phase-sensitive detectors at 45 degree intervals. Using 4 instead of just 2 allows us to cancel the third harmonic of the

  9. // coil frequency.


  10. // Timer 2 will be used to generate a tone for the earpiece or headset. (таймер 2 используется для генерации тона для наушников)


  11. // Other division ratios for timer 1 are possible, from about 235 upwards.


  12. // Wiring:

  13. // Connect digital pin 4 (alias T0) to digital pin 9

  14. // Connect digital pin 5 through resistor to primary coil and tuning capacitor

  15. // Connect output from receive amplifier to analog pin 0. Output of receive amplifier should be biased to about half of the analog reference.

  16. // When using USB power, change analog reference to the 3.3V pin, because there is too much noise on the +5V rail to get good sensitivity.

  17. #include <LiquidCrystal.h>

  18. #include <LcdBarGraph.h>

  19. #define max_ampAverage 200

  20. LiquidCrystal lcd(6, 7, 10, 11, 12, 13);

  21. LcdBarGraph lbg(&lcd, 16, 0, 1);


  22. #define TIMER1_TOP  (259)        // can adjust this to fine-tune the frequency to get the coil tuned (see above) (это значение используется для точной настройки частоты катушки)


  23. #define USE_3V3_AREF  (1)        // set to 1 of running on an Arduino with USB power, 0 for an embedded atmega28p with no 3.3V supply available


  24. // Digital pin definitions

  25. // Digital pin 0 not used, however if we are using the serial port for debugging then it’s serial input

  26. const int debugTxPin = 1;        // transmit pin reserved for debugging (передающий контакт, зарезервированный для целей отладки)

  27. const int encoderButtonPin = 2;  // encoder button, also IN0 for waking up from sleep mode

  28. const int earpiecePin = 3;       // earpiece, aka OCR2B for tone generation

  29. const int T0InputPin = 4;

  30. const int coilDrivePin = 5;

  31. const int LcdRsPin = 6;

  32. const int LcdEnPin = 7;

  33. const int LcdPowerPin = 8;       // LCD power and backlight enable

  34. const int T0OutputPin = 9;

  35. const int lcdD4Pin = 10;

  36. const int lcdD5Pin = 11;         // pins 11-13 also used for ICSP

  37. const int LcdD6Pin = 12;

  38. const int LcdD7Pin = 13;


  39. // Analog pin definitions (используемые аналоговые контакты)

  40. const int receiverInputPin = 0;

  41. const int encoderAPin = A1;

  42. const int encoderBpin = A2;

  43. // Analog pins 3-5 not used


  44. // Variables used only by the ISR

  45. int16_t bins[4];                 // bins used to accumulate ADC readings, one for each of the 4 phases (используются для хранения значений, считываемых с АЦП)

  46. uint16_t numSamples = 0;

  47. const uint16_t numSamplesToAverage = 1024;


  48. // Variables used by the ISR and outside it

  49. volatile int16_t averages[4];    // when we’ve accumulated enough readings in the bins, the ISR copies them to here and starts again

  50. volatile uint32_t ticks = 0;     // system tick counter for timekeeping

  51. volatile bool sampleReady = false;  // indicates that the averages array has been updated


  52. // Variables used only outside the ISR

  53. int16_t calib[4];                // values (set during calibration) that we subtract from the averages (значения, устанавливаемые во время калибровки, в дальнейшем мы их вычитаем из средних значений)


  54. volatile uint8_t lastctr;

  55. volatile uint16_t misses = 0;    // this counts how many times the ISR has been executed too late. Should remain at zero if everything is working properly.


  56. const double halfRoot2 = sqrt(0.5);

  57. const double quarterPi = 3.1415927/4.0;

  58. const double radiansToDegrees = 180.0/3.1415927;


  59. // The ADC sample and hold occurs 2 ADC clocks (= 32 system clocks) after the timer 1 overflow flag is set.

  60. // This introduces a slight phase error, which we adjust for in the calculations.

  61. const float phaseAdjust = (45.0 * 32.0)/(float)(TIMER1_TOP + 1);


  62. float threshold = 5.0;          // lower = greater sensitivity. 10 is just about usable with a well-balanced coil.

  63.                                  // The user will be able to adjust this via a pot or rotary encoder. (эту границу можно сделать настраиваемой с помощью потенциометра или энкодера)


  64. void setup()

  65. {

  66.   lcd.begin(16, 2);// LCD 16X2

  67.   pinMode(encoderButtonPin, INPUT_PULLUP);  

  68.   digitalWrite(T0OutputPin, LOW);

  69.   pinMode(T0OutputPin, OUTPUT);       // pulse pin from timer 1 used to feed timer 0

  70.   digitalWrite(coilDrivePin, LOW);

  71.   pinMode(coilDrivePin, OUTPUT);      // timer 0 output, square wave to drive transmit coil

  72.   

  73.   cli();

  74.   // Stop timer 0 which was set up by the Arduino core

  75.   TCCR0B = 0;        // stop the timer

  76.   TIMSK0 = 0;        // disable interrupt

  77.   TIFR0 = 0x07;      // clear any pending interrupt

  78.   

  79.   // Set up ADC to trigger and read channel 0 on timer 1 overflow

  80. #if USE_3V3_AREF

  81.   ADMUX = (1 << ADLAR);                   // use AREF pin (connected to 3.3V) as voltage reference, read pin A0, left-adjust result

  82. #else

  83.   ADMUX = (1 << REFS0) | (1 << ADLAR);    // use Avcc as voltage reference, read pin A0, left-adjust result

  84. #endif  

  85.   ADCSRB = (1 << ADTS2) | (1 << ADTS1);   // auto-trigger ADC on timer/counter 1 overflow

  86.   ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADPS2);  // enable adc, enable auto-trigger, prescaler = 16 (1MHz ADC clock)

  87.   DIDR0 = 1;


  88.   // Set up timer 1.

  89.   // Prescaler = 1, phase correct PWM mode, TOP = ICR1A

  90.   TCCR1A = (1 << COM1A1) | (1 << WGM11);

  91.   TCCR1B = (1 << WGM12) | (1 << WGM13) | (1 << CS10);    // CTC mode, prescaler = 1

  92.   TCCR1C = 0;

  93.   OCR1AH = (TIMER1_TOP/2 >> 8);

  94.   OCR1AL = (TIMER1_TOP/2 & 0xFF);

  95.   ICR1H = (TIMER1_TOP >> 8);

  96.   ICR1L = (TIMER1_TOP & 0xFF);

  97.   TCNT1H = 0;

  98.   TCNT1L = 0;

  99.   TIFR1 = 0x07;      // clear any pending interrupt

  100.   TIMSK1 = (1 << TOIE1);


  101.   // Set up timer 0

  102.   // Clock source = T0, fast PWM mode, TOP (OCR0A) = 7, PWM output on OC0B

  103.   TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);

  104.   TCCR0B = (1 << CS00) | (1 << CS01) | (1 << CS02) | (1 << WGM02);

  105.   OCR0A = 7;

  106.   OCR0B = 3;

  107.   TCNT0 = 0;

  108.   sei();

  109.   

  110.   while (!sampleReady) {}    // discard the first sample (отбрасываем первый отсчет)

  111.   misses = 0;

  112.   sampleReady = false;

  113.   

  114.   Serial.begin(19200);

  115. }


  116. // Timer 0 overflow interrupt (прерывание от таймера 0). This serves 2 purposes: (служит для 2-х целей)

  117. // 1. It clears the timer 0 overflow flag. If we don’t do this, the ADC will not see any more Timer 0 overflows and we will not get any more conversions.

  118. // 2. It increments the tick counter, allowing is to do timekeeping. We get 62500 ticks/second.

  119. // We now read the ADC in the timer interrupt routine instead of having a separate comversion complete interrupt.

  120. ISR(TIMER1_OVF_vect)

  121. {

  122.   ++ticks;

  123.   uint8_t ctr = TCNT0;

  124.   int16_t val = (int16_t)(uint16_t)ADCH;    // only need to read most significant 8 bits (нам нужно считывать только 8 наиболее значащих бит)

  125.   if (ctr != ((lastctr + 1) & 7))

  126.   {

  127.     ++misses;

  128.   }

  129.   lastctr = ctr;

  130.   int16_t *p = &bins[ctr & 3];

  131.   if (ctr < 4)

  132.   {

  133.     *p += (val);

  134.     if (*p > 15000) *p = 15000;

  135.   }

  136.   else

  137.   {

  138.     *p -= val;

  139.     if (*p < –15000) *p = –15000;

  140.   }

  141.   if (ctr == 7)

  142.   {

  143.     ++numSamples;

  144.     if (numSamples == numSamplesToAverage)

  145.     {

  146.       numSamples = 0;

  147.       if (!sampleReady)      // if previous sample has been consumed

  148.       {

  149.         memcpy((void*)averages, bins, sizeof(averages));

  150.         sampleReady = true;

  151.       }

  152.       memset(bins, 0, sizeof(bins));

  153.     }

  154.   }

  155. }


  156. void loop()

  157. {

  158.   while (!sampleReady) {}

  159.   uint32_t oldTicks = ticks;

  160.   

  161.   if (digitalRead(encoderButtonPin) == LOW)

  162.   {

  163.     // Calibrate button pressed. We save the current phase detector outputs and subtract them from future results.

  164.     // This lets us use the detector if the coil is slightly off-balance.

  165.     // It would be better to everage several samples instead of taking just one.

  166.     for (int i = 0; i < 4; ++i)

  167.     {

  168.       calib[i] = averages[i];

  169.     }

  170.     sampleReady = false;

  171.     Serial.print(“Calibrated: “);

  172.    

  173.     lcd.setCursor(0,0);

  174.     lcd.print(“Calibrating…  “);   

  175.     for (int i = 0; i < 4; ++i)

  176.     {

  177.       Serial.write(‘ ‘);

  178.       

  179.       Serial.print(calib[i]);

  180.    

  181.     lcd.setCursor(0,1);   

  182.     lcd.print(‘ ‘);   

  183.     lcd.print(calib[4]);

  184.     lcd.print(”        “);     

  185.     }

  186.     Serial.println();

  187.   }

  188.   else

  189.   {  

  190.     for (int i = 0; i < 4; ++i)

  191.     {

  192.       averages[i] -= calib[i];

  193.     }

  194.     const double f = 200.0;

  195.    

  196.     // Massage the results to eliminate sensitivity to the 3rd harmonic, and divide by 200

  197.     double bin0 = (averages[0] + halfRoot2 * (averages[1] – averages[3]))/f;

  198.     double bin1 = (averages[1] + halfRoot2 * (averages[0] + averages[2]))/f;

  199.     double bin2 = (averages[2] + halfRoot2 * (averages[1] + averages[3]))/f;

  200.     double bin3 = (averages[3] + halfRoot2 * (averages[2] – averages[0]))/f;

  201.     sampleReady = false;          // we’ve finished reading the averages, so the ISR is free to overwrite them again


  202.     double amp1 = sqrt((bin0 * bin0) + (bin2 * bin2));

  203.     double amp2 = sqrt((bin1 * bin1) + (bin3 * bin3));

  204.     double ampAverage = (amp1 + amp2)/2.0;

  205.    

  206.     // The ADC sample/hold takes place 2 clocks after the timer overflow

  207.     double phase1 = atan2(bin0, bin2) * radiansToDegrees + 45.0;

  208.     double phase2 = atan2(bin1, bin3) * radiansToDegrees;

  209.   

  210.     if (phase1 > phase2)

  211.     {

  212.       double temp = phase1;

  213.       phase1 = phase2;

  214.       phase2 = temp;

  215.     }

  216.    

  217.     double phaseAverage = ((phase1 + phase2)/2.0) – phaseAdjust;

  218.     if (phase2 – phase1 > 180.0)

  219.     {

  220.       if (phaseAverage < 0.0)

  221.       {

  222.         phaseAverage += 180.0;

  223.       }

  224.       else

  225.       {

  226.         phaseAverage -= 180.0;

  227.       }

  228.     }

  229.         

  230.     // For diagnostic purposes, print the individual bin counts and the 2 indepedently-calculated gains and phases                                                        

  231.     Serial.print(misses);

  232.     Serial.write(‘ ‘);

  233.    

  234.     if (bin0 >= 0.0) Serial.write(‘ ‘);

  235.     Serial.print(bin0, 2);

  236.     Serial.write(‘ ‘);

  237.     if (bin1 >= 0.0) Serial.write(‘ ‘);

  238.     Serial.print(bin1, 2);

  239.     Serial.write(‘ ‘);

  240.     if (bin2 >= 0.0) Serial.write(‘ ‘);

  241.     Serial.print(bin2, 2);

  242.     Serial.write(‘ ‘);

  243.     if (bin3 >= 0.0) Serial.write(‘ ‘);

  244.     Serial.print(bin3, 2);

  245.     Serial.print(”    “);

  246.     Serial.print(amp1, 2);

  247.     Serial.write(‘ ‘);

  248.     Serial.print(amp2, 2);

  249.     Serial.write(‘ ‘);

  250.     if (phase1 >= 0.0) Serial.write(‘ ‘);

  251.     Serial.print(phase1, 2);

  252.     Serial.write(‘ ‘);

  253.     if (phase2 >= 0.0) Serial.write(‘ ‘);

  254.     Serial.print(phase2, 2);

  255.     Serial.print(”    “);

  256.    

  257.     // Print the final amplitude and phase, which we use to decide what (if anything) we have found)

  258.     if (ampAverage >= 0.0) Serial.write(‘ ‘);

  259.     Serial.print(ampAverage, 1);

  260.     Serial.write(‘ ‘);

  261.         

  262.         lcd.setCursor(0,0);

  263.         lcd.print(”          “);

  264.         lcd.print(ampAverage);        

  265.         lcd.setCursor(0,1);

  266.         lbg.drawValue(ampAverage, max_ampAverage);


  267.     if (phaseAverage >= 0.0) Serial.write(‘ ‘);

  268.     Serial.print((int)phaseAverage);

  269.    

  270.     // Decide what we have found and tell the user

  271.     if (ampAverage >= threshold)

  272.     {

  273.       // When held in line with the centre of the coil:

  274.       // – non-ferrous metals give a negative phase shift, e.g. -90deg for thick copper or aluminium, a copper olive, -30deg for thin alumimium.

  275.       // Ferrous metals give zero phase shift or a small positive phase shift.

  276.       // So we’ll say that anything with a phase shift below -20deg is non-ferrous.

  277.       if (phaseAverage < –20.0)

  278.       {

  279.         Serial.print(” Non-ferrous”);

  280.         lcd.setCursor(0,0);        

  281.         lcd.print(“NonFerous “);      

  282.      

  283.       }

  284.       else

  285.       {

  286.         Serial.print(” Ferrous”);        

  287.         lcd.setCursor(0,0);      

  288.         lcd.print(“Ferrous    “);                 

  289.       }

  290.       float temp = ampAverage;

  291.               

  292.        int thisPitch = map (temp, 10, 200, 100, 1500);

  293.        tone(3, thisPitch,120);

  294.       

  295.       while (temp > threshold)

  296.       {

  297.         Serial.write(‘!’);      

  298.         temp -= (threshold/2);

  299.       }

  300.     }   

  301.     Serial.println();

  302.    

  303.    }

  304.   while (ticks – oldTicks < 8000)

  305.   {

  306.   }      

  307. }
复制代码
The LcdBarGraph library used in this sketch can be downloaded from this link . The program code makes extensive use of timers, if you are not very good at these, we recommend reading the Arduino Timers Guide for Beginners .
Video demonstrating the circuit in operation
http://bilibili.com/video/BV1G6ScBUE4W/?spm_id_from=333.337.search-card.all.click

中文的翻译如下:
本文将介绍如何基于 Arduino 开发板制作一款能够区分不同金属的金属探测器。这款金属探测器能够探测到深度达 15 厘米的小型金属物体(例如硬币),以及深度达 50 厘米(甚至更深)的大型金属物体。它还能区分含铁金属(铁质金属)和非铁质金属(非铁质金属)。该金属探测器设计简洁,同时又具备令人满意的灵敏度。

此前,我们在网站上介绍过一个 基于 Arduino 的简易金属探测器项目 ,建议大家阅读一下,以了解金属探测器的工作原理。

必要组件
  • Arduino Nano 开发板
  • LCD 显示屏 16×2
  • 运算放大器 TL081 或 741、LT1677 也适用(项目作者使用)
  • 扬声器 0.25 瓦,8 欧姆。
  • 通用型NPN晶体管。
  • 感应线圈 – 2 个
  • 10 kΩ 电位器
  • 电容器和电阻器(如下图所示)。
  • 开关。
  • 电池。

项目方案

下图所示为基于 Arduino 的金属探测器电路图,该电路具有金属识别功能。

当检测到金属时,该设备会发出声音信号,液晶显示屏会用条形图显示金属的接近程度,并指示金属的类型——铁(铁质)或非铁质(非铁质)。

该装置是一款工作频率极低(VLF)的感应平衡式金属探测器。金属探测器包含一个发射电感线圈和一个接收电感线圈。与所有类似探测器的电路一样,线圈间的平衡对于我们的装置至关重要。探测器电路中的电位器用于消除信号中反相分量的影响——它将相位偏移降至零,而同相分量则通过适当排列线圈来使其降至零——这符合感应平衡式探测器的工作原理。


每个感应线圈均由64匝0.5平方毫米的漆包铜线绕制而成,绕线形状为直径11厘米的D形。线圈结构用胶带包裹,并用铝箔屏蔽,之后将镀锡铜线连接到铝箔上——需要在铝箔上留出一个小缝隙以便连接。最后,将两个线圈固定在塑料底座上。金属探测器感应线圈的组装外观如下图所示。

您可以在文章末尾提供的视频中更详细地了解组装过程。组装完成后的金属探测器结构如下图所示。

要搭建这个项目,首先需要确定电路中振荡电路的谐振频率。你可以使用物理课上学到的公式、在线计算器,或者用示波器测量。如果你按照说明组装了线圈,那么电路的谐振频率应该约为 7.64 kHz。如果你得到的谐振频率值不同,则需要对程序的下一行进行相应的修改:

正如您在下方视频中所见,金属探测器的探测结果令人印象深刻。在没有金属的情况下,该设备运行稳定。直径为15厘米的金属圆片可在30厘米以上的距离被探测到。较大的金属物体可在40-50厘米以上的距离被探测到。一枚小硬币(在空中)可在15厘米的距离被探测到。

为了给金属探测器供电,项目作者使用了两节锂电池,串联后可提供 7.4V 的供电电压——该电压被施加到 Arduino 板的 Vin 引脚。该设备的功耗不超过 20mA,因此使用这样的电池可以长时间工作。

为了显著提高金属探测器的灵敏度,该项目的作者提议使用大功率 MOSFET 晶体管来控制发射线圈——未来,他计划在项目页面上公布此类实验的结果。


本程序中使用的 LcdBarGraph 库
LcdBarGraph-2.0.1.zip (100.2 KB, 下载次数: 0) 下载 。程序代码大量使用了定时器,如果您对定时器不太熟悉,我们建议您阅读 《Arduino 定时器入门指南》 。
视频演示了电路的运行情况

评分

参与人数 2黑币 +80 收起 理由
wpppmlah + 30 共享资料的奖励!
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表