单片机论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1046|回复: 3
收起左侧

BMP180测量海拔高度传感器单片机程序+资料

[复制链接]
linluo 发表于 2020-4-23 13:49 | 显示全部楼层 |阅读模式
在测量海拔高度时,传统的做法是通过测量某一高度的大气压力,再经过换算才能得到高度数据。为了测量大气压力,就得用上气压传感器,下面就来讨论一下气压传感器的应用。
气压传感器是压力传感器中的一种,它专用于测量气体的绝对压强。目前市场上能见到的气压传感器有很多种,下面就以市场上常见的Bosch公司推出的BMP180来进行讨论。BMP180不仅可以实时的测量大气压力,还能测量实时温度。同时它还具有IIC总线的接口,便于单片机进行访问。另外它的使用也很方便,不需要太多的操作就可读取到气压及测量数据。
BMP180采用强大的8脚陶瓷无引线芯片承载(LCC)超薄封装,它性能卓越,内置有校准补偿,绝对精度最低可以达到0.03hPa(0.25米),并且耗电极低,只有3μA。气压测量范围从300hPa到1100hPa,换算成高度为海拔9000米到500米。下图是其封装外形和引脚排列。
      
引脚各功能如下:1脚(GND)接电源地,2脚(EOC)为完成转换输出,3脚(VDDA)为正电源,4脚(VDDD)为数字正电源,5脚为空,6脚(SCL)为IIC的时钟端,7脚(SDA)为IIC的数据端,8脚(XCLR)为主清除信号输入端,低电平有效,用来复位BMP180和初始化寄存器和控制器,在不用的情况下可以空置。
BMP180的工作电压为1.8V~3.6V,典型工作电压为2.5V,其与单片机相连的典型电路如下图所示。
从上图中可以看到,BMP180内包含有电阻式压力传感器、AD转换器和控制单元,其中控制单元包括了EEPROM和IIC接口。读取BMP180时会直接传送没有经过补偿的温度值和压力值。而在EEPROM中则储存了176位单独的校准数据,这些数据将对读取的温度压力值进行补偿。176位的EEPROM被划分为11个字,每个字16位,这样就包含有11个校准系数。每个器件模块都有自己单独的校准系数,在第一次计算温度压力数据之前,单片机就应该先读出读出EEPROM中的这些校准数据,然后再开始采集数据温度和压力数据。
和所有的IIC总线器件一样,BMP180也有一个器件的固定地址,根据其数据手册,出厂时默认BMP180的从机地址为0xEE(写入方向),或0xEF(读出方向)。温度数据UT和压力数据UP都存储在寄存器的第0到15位之中,压力数据UP的精度还可扩展至16~19位。

  
上图中左边是Bosch公司技术手册上提供的读取顺序的流程图,右边是EEPROM中的校准数据。
从流程图中可以看出,单片机发送开始信号启动温度和压力测量,经过一定的转换时间(4.5ms)后,从IIC接口读出结果。为了将温度的单位换算成℃和将压力的单位换算成hPa,需要用到EEPROM中的校准数据来进行补偿计算,这些数据也可以从IIC接口读出。事实上,EEPROM中的这些校准数据应该在程序初始化的时候就读出,以方便后面的计算。 在同一个采样周期中BMP180可以采128次压力值和1次温度值,并且这些值在读取后会被及时更新掉。若不想等待到最大转化时间之后才读取数据,可以有效利用BMP180的输出管脚EOC来检查转化是否完毕。若为1表示转换完成,为0表示转换正在进行中。
要得到温度或气压的值,必须要访问地址为0xF4的控制寄存器。它根据写入数据的不同,回应的值也不一样,具体如下表所示。
从图中可以看出,要获得温度数据,必须先向控制寄存器(地址0xF4)写0x2E,然后等待至少4.5ms,才可以从地址0xF6和0xF7读取十六位的温度数据。同样,要获得气压数据,必须先向控制寄存器(地址0xF4)写0x34,然后等待至少4.5ms,才可以从地址0xF6和0xF7读取16位的气压数据,若要扩展分辨率,还可继续读取0xF8(XLSB)扩展16位数据到19位。获取到的数据还要根据EEPROM中的校准数据来进行补偿后才能用,EEPROM的数据读取可根据上图中的地址来进行,地址从0xAA~0xBF,具体的补偿算法可参看官方的数据手册,这里就不赘述了。
下面以一个例子来看一下BMP180的具体应用。
例子:利用单片机读取来自BMP180的温度和气压数据,并把它们通过LCD1602显示出来。
BMP180的SDA、SCL端分别接到ATMega16的TWI端(PC1、PC0),EOC和XCLR端悬空,LCD1602的接法与前面的一致。参考代码如下。
  1. #include <iom16.h>
  2. //=========================定义从器件地址和读写方式=============================
  3. #define rd_device_add 0xef //即11101111,1110111是BMP180器件的固定地址,最后的1表示对从器件进行读操作
  4. #define wr_device_add 0xee //即11101110,1110111是BMP180器件的固定地址,最后的0表示对从器件时行写操作
  5. //===============================TWI状态定义==================================
  6. #define START 0x08
  7. #define RE_START  0x10
  8. #define MT_SLA_ACK  0x18
  9. #define MT_SLA_NOACK  0x20
  10. #define MT_DATA_ACK 0x28
  11. #define MT_DATA_NOACK 0x30
  12. #define MR_SLA_ACK  0x40
  13. #define MR_SLA_NOACK  0x48
  14. #define MR_DATA_ACK 0x50
  15. #define MR_DATA_NOACK 0x58
  16. //=============================常用TWI操作定义================================
  17. #define Start() (TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN))
  18. #define Stop()  (TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN))
  19. #define Wait()  {while(!(TWCR&(1<<TWINT)));}
  20. #define TestAck() (TWSR&0xf8)
  21. #define SetAck()  (TWCR|=(1<<TWEA))
  22. #define SetNoAck()  (TWCR&=~(1<<TWEA))
  23. #define Twi() (TWCR=(1<<TWINT)|(1<<TWEN))
  24. #define Write8Bit(x)  {TWDR=(x);TWCR=(1<<TWINT)|(1<<TWEN);}
  25. //============引脚电平的宏定义===============
  26. #define LCM_RS_1 PORTB_Bit0=1     //RS脚输出高电平
  27. #define LCM_RS_0 PORTB_Bit0=0       //RS脚输出低电平
  28. #define LCM_RW_1 PORTB_Bit1=1       //RW脚输出高电平
  29. #define LCM_RW_0 PORTB_Bit1=0       //RW脚输出低电平
  30. #define LCM_EN_1 PORTB_Bit2=1      //EN脚输出高电平  
  31. #define LCM_EN_0 PORTB_Bit2=0       //EN脚输出低电平
  32. #define DataPort PORTA          //PORTA为数据端口
  33. #define Busy 0x80         //忙信号
  34. //==============定义全局变量================
  35. unsigned char ge,shi,bai,qian,wan,shiwan;    //显示变量
  36. unsigned char ReadTemp[2];                   //接收到的温度数据缓冲区
  37. unsigned char ReadPressure[2];              //接收到的气压数据缓冲区
  38. int ac1;
  39. int ac2;
  40. int ac3;
  41. unsigned int ac4;
  42. unsigned int ac5;
  43. unsigned int ac6;
  44. int b1;
  45. int b2;
  46. int mb;
  47. int mc;
  48. int md;
  49. //==============定义显示字符串================
  50. const unsigned char str0[]={"  T:   .   C    "};    //显示温度
  51. const unsigned char str1[]={"  P:   .   Kpa  "};    //显示气压
  52. //===============1mS延时===================
  53. void delay_1ms(void)  
  54. {   
  55.   unsigned int i;
  56.   for(i=1;i<(unsigned int)(8*143-2);i++)
  57.     ;
  58. }
  59. //=============n*1mS延时===============
  60. void delay_nms(unsigned int n)  
  61. {
  62.   unsigned int i=0;
  63.   while(i<n)
  64.    {delay_1ms();
  65.     i++;
  66.    }
  67. }
  68. //===============IIC总线写n个字节(成功返回0,失败返回1)=====================
  69. unsigned char I2C_Write(unsigned char RomAddress,unsigned char *buf,unsigned char len)
  70. {
  71.   unsigned char i;
  72.   Start();                                                  //启动I2C总线
  73.   Wait();                                                   //等待回应
  74.   if(TestAck()!=START)                        
  75.     return 1;                                              //若回应的不是启动信号,则失败返回1
  76.   Write8Bit(wr_device_add);                    //写I2C从器件地址、写方向
  77.   Wait();                                                   //等待回应
  78.   if(TestAck()!=MT_SLA_ACK)                 
  79.     return 1;                                               //若回应的不是ACK信号,则失败返回值1                                
  80.   Write8Bit(RomAddress);                         //写BMP180的ROM地址
  81.   Wait();                                                     //等待回应
  82.   if(TestAck()!=MT_DATA_ACK)
  83.     return 1;                                                 //若回应的不是ACK信号则失败返回值1
  84.   for(i=0;i<len;i++)
  85.     {
  86.       Write8Bit(buf[i]);                          //写数据到BMP180的ROM中
  87.       Wait();                                                      //等待回应
  88.       if(TestAck()!=MT_DATA_ACK)
  89.         {return 1;}                                     //若回应的不是ACK信号则失败返回值1
  90.       delay_nms(10);
  91.     }
  92.   Stop();                                                      //停止I2C总线
  93.   delay_nms(10);                                          //延时等待BMP180写完
  94.   return 0;                                                   //写入成功,返回值0
  95. }
  96. //====================IIC总线读n个字节(成功返回0,失败返回1)=========================
  97. unsigned char I2C_Read(unsigned char RomAddress,unsigned char *buf,unsigned char len)
  98. {
  99.   unsigned char i;
  100.   Start();                                                     //启动I2C总线
  101.   Wait();                                                      //等待回应
  102.   if(TestAck()!=START)
  103.     return 1;                                                  //若回应的不是启动信号,则失败返回1
  104.   Write8Bit(wr_device_add);                        //写I2C从器件地址、写方向
  105.   Wait();                                                       //等待回应
  106.   if(TestAck()!=MT_SLA_ACK)
  107.     return 1;                                                   //若回应的不是ACK信号,则失败返回值1
  108.   Write8Bit(RomAddress);                             //写BMP180的ROM地址
  109.   Wait();                                                         //等待回应
  110.   if(TestAck()!=MT_DATA_ACK)
  111.     return 1;                                                     //若回应的不是ACK信号,则失败返回值1
  112.   Start();                                                          //重新启动I2C总线
  113.   Wait();                                                           //等待回应
  114.   if(TestAck()!=RE_START)
  115.     return 1;                                                      //若回应的不是重复启动信号,则失败返回1
  116.   Write8Bit(rd_device_add);                            //写I2C从器件地址、读方向
  117.   Wait();                                                           //等待回应
  118.   if(TestAck()!=MR_SLA_ACK)
  119.     return 1;                                                       //若回应的不是ACK信号,则失败返回值1
  120.   for(i=0;i<len;i++)
  121.     {
  122.       Twi();                                                              //启动I2C读方式
  123.       SetAck();                                           //设置接收自动应答
  124.       delay_nms(10);
  125.       Wait();                                                            //等待回应
  126.       delay_nms(10);
  127.       *(buf+i)=TWDR;                                  //把连续读取的len个字节数据依次存入对应的地址单元(数组)中
  128.     }
  129.   SetNoAck();                                         //读数据的最后一位后紧跟的是无应答
  130.   delay_nms(10);
  131.   Stop();                                                            //停止I2C总线
  132.   return 0;                                                    //成功返回值0
  133. }
  134. //================检测LCD忙信号子函数================
  135. void WaitForEnable(void)
  136. {
  137.     unsigned char val;
  138.     DataPort=0xff;        //数据线电平拉高
  139.     LCM_RS_0;             //选择指令寄存器
  140.     LCM_RW_1;             //选择写方式
  141.     __asm("NOP");         //调用汇编指令延时一个空指令周期,等待稳定
  142.     LCM_EN_1;             //使能端拉高电平
  143.     __asm("NOP");
  144.     __asm("NOP");         //调用汇编指令延时两个空指令周期,等待稳定
  145.     DDRA=0x00;            //改变数据线方向成输入
  146.     val=PINA;             //读取数据
  147.     while(val&Busy)
  148.       val=PINA;           //当DB7位为1时表示忙,循环检测
  149.     LCM_EN_0;             //忙信号结束,拉低使能端电平
  150.     DDRA=0xff;            //改变数据线方向成输出
  151. }
  152. //================写数据到LCD子函数=================
  153. void LcdWriteData(unsigned char dataW)      //写数据dataW到LCD中
  154. {
  155.     WaitForEnable();        //检测忙信号
  156.     LCM_RS_1;               //选择数据寄存器   
  157.     LCM_RW_0;               //选择读方式
  158.     __asm("NOP");           //调用汇编指令延时一个空指令周期,等待稳定
  159.     DataPort=dataW;         //把显示数据送到数据线上
  160.     __asm("NOP");           //调用汇编指令延时一个空指令周期,等待稳定
  161.     LCM_EN_1;               //使能端拉高电平
  162.     __asm("NOP");
  163.     __asm("NOP");           //调用汇编指令延时两个空指令周期,等待稳定
  164.     LCM_EN_0;               //拉低使能端,执行写入动作
  165. }
  166. //================写命令到LCD子函数================
  167. void LcdWriteCommand(unsigned char CMD,unsigned char Attribc)   //写命令CMD到LCD中,Arribc为1时检测忙信号,否则不检测
  168. {
  169.     if(Attribc)
  170.       WaitForEnable();        //检测忙信号
  171.     LCM_RS_0;                 //选择指令寄存器
  172.     LCM_RW_0;                 //选择写方式
  173.     __asm("NOP");             //调用汇编指令延时一个空指令周期,等待稳定
  174.     DataPort=CMD;             //把命令数据送到数据线上
  175.     __asm("NOP");             //调用汇编指令延时一个空指令周期,等待稳定   
  176.     LCM_EN_1;                 //使能端拉高电平
  177.     __asm("NOP");
  178.     __asm("NOP");             //调用汇编指令延时两个空指令周期,等待稳定
  179.     LCM_EN_0;                 //拉低使能端,执行写入动作
  180. }
  181. //================显示光标定位子函数================
  182. void LocateXY(char posx,char posy)      //定位位置到地址x列y行
  183. {
  184.     unsigned char temp;
  185.     temp=posx&0x0f;       //屏蔽高4位,限定x坐标的范围为0~15
  186.     posy&=0x01;           //屏蔽高7位,限定y坐标的范围为0~1
  187.     if(posy)        
  188.       temp|=0x40;         //若要显示的是第二行,则地址码+0x40,因为第二行起始地址为0x40
  189.     temp|=0x80;           //指令码为地址码+0x80,因为写DDRAM时DB7恒为1(即0x80)
  190.     LcdWriteCommand(temp,1);    //把temp写入LCD中,检测忙信号
  191. }
  192. //===========显示指定座标的一个字符子函数============
  193. void DisplayOneChar(unsigned char x,unsigned char y,unsigned char Wdata)    //在x列y行处显示变量Wdata中的一个字符
  194. {
  195.     LocateXY(x,y);          //定位要显示的位置
  196.     LcdWriteData(Wdata);    //将要显示的数据Wdata写入LCD
  197. }
  198. //==========显示指定座标的一串字符子函数===========
  199. void ePutstr(unsigned char x,unsigned char y,unsigned char const *ptr)    //在x列y行处显示ptr指向的字符串
  200. {
  201.     unsigned char i,j=0;
  202.     while(ptr[j]>31)
  203.       j++;                         //ptr[j]>31时为ASCII码,j累加,计算出字符串长度
  204.     for(i=0;i<j;i++)
  205.     {
  206.       DisplayOneChar(x++,y,ptr[i]);       //显示单个字符,同时x坐标递增
  207.       if(x==16)
  208.         {
  209.           x=0;
  210.           y^=1;         //当每行显示超过16个字符时换行继续显示
  211.         }
  212.     }
  213. }
  214. //==================LCD初始化子函数==================
  215. void InitLcd(void)
  216. {
  217.     LcdWriteCommand(0x38,0);      //8位数据方式,双行显示,5X7字形,不检测忙信号
  218.     delay_nms(5);                          //延时5ms
  219.     LcdWriteCommand(0x38,0);
  220.     delay_nms(5);
  221.     LcdWriteCommand(0x38,0);
  222.     delay_nms(5);                          //重复三次
  223.     LcdWriteCommand(0x38,1);      //8位数据方式,双行显示,5X7字形,检测忙信号
  224.     LcdWriteCommand(0x08,1);      //关闭显示,检测忙信号
  225.     LcdWriteCommand(0x01,1);      //清屏,检测忙信号
  226.     LcdWriteCommand(0x06,1);      //显示光标右移设置,检测忙信号
  227.     LcdWriteCommand(0x0C,1);      //打开显示,光标不显示,不闪烁,检测忙信号
  228. }
  229. //==================转换子函数====================
  230. void conversion(long temp_data)  
  231. {  
  232.     shiwan=temp_data/100000+0x30 ;
  233.     temp_data=temp_data%100000;   //取余运算
  234.     wan=temp_data/10000+0x30 ;
  235.     temp_data=temp_data%10000;   //取余运算
  236.     qian=temp_data/1000+0x30 ;
  237.     temp_data=temp_data%1000;    //取余运算
  238.     bai=temp_data/100+0x30   ;
  239.     temp_data=temp_data%100;     //取余运算
  240.     shi=temp_data/10+0x30    ;
  241.     temp_data=temp_data%10;      //取余运算
  242.     ge=temp_data+0x30;  
  243. }
  244. //===================BMP180读温度=====================
  245. void BMP180ReadTemp(void)
  246. {
  247.   unsigned char t=0x2e;  
  248.   I2C_Write(0xf4,&t,1);                          //向地址0xf4写0x2e,进行温度转换
  249.   delay_nms(5);                                    //延时大于4.5ms
  250.   I2C_Read(0xf6,ReadTemp,2);             //从地址0xf6开始读出温度数据并存到数组ReadTemp中,共2个字节
  251. }
  252. //===================BMP180读气压=====================
  253. void BMP180ReadPressure(void)
  254. {
  255.   unsigned char t=0x34;
  256.   I2C_Write(0xf4,&t,1);                             //向地址0xf4写0x34,进行第一次气压转换
  257.   delay_nms(5);                                      //延时大于4.5ms
  258.   I2C_Read(0xf6,ReadPressure,2);         //从地址0xf6开始读出气压数据并存到数组ReadPressure中,共2个字节
  259. }

  260. //==================初始化BMP180====================
  261. void Init_BMP180(void)
  262. {
  263.   unsigned char temp[2];
  264.   I2C_Read(0xaa,temp,2);
  265.   ac1 = (temp[0]<<8)|temp[1];
  266.   I2C_Read(0xac,temp,2);
  267.   ac2 = (temp[0]<<8)|temp[1];
  268.   I2C_Read(0xae,temp,2);
  269.   ac3 = (temp[0]<<8)|temp[1];
  270.   I2C_Read(0xb0,temp,2);
  271.   ac4 = (temp[0]<<8)|temp[1];
  272.   I2C_Read(0xb2,temp,2);
  273.   ac5 = (temp[0]<<8)|temp[1];
  274.   I2C_Read(0xb4,temp,2);
  275.   ac6 = (temp[0]<<8)|temp[1];
  276.   I2C_Read(0xb6,temp,2);
  277.   b1 = (temp[0]<<8)|temp[1];
  278.   I2C_Read(0xb8,temp,2);
  279.   b2 = (temp[0]<<8)|temp[1];
  280.   I2C_Read(0xba,temp,2);
  281.   mb = (temp[0]<<8)|temp[1];
  282.   I2C_Read(0xbc,temp,2);
  283.   mc = (temp[0]<<8)|temp[1];
  284.   I2C_Read(0xbe,temp,2);
  285.   md = (temp[0]<<8)|temp[1];         //连续读取EEPROM中的校准数据,并存放到相应的变量中,以供后面补偿使用
  286. }
  287. //===================转换子函数=====================
  288. void BMP180Convert()
  289. {
  290.   long ut,up,temperature,pressure;    //定义长整型变量
  291.   long x1, x2, b5, b6, x3, b3, p;
  292.   unsigned long b4, b7;           //定义无符号长整型变量
  293.   BMP180ReadTemp();         //读取温度
  294.   ut=ReadTemp[0]<<8|ReadTemp[1];  //合成温度数据
  295.   x1=((long)ut-ac6)*ac5>>15;      //以下根据EEPROM中的值对获取的温度数据的进行补偿换算
  296.   x2=((long)mc<<11)/(x1+md);
  297.   b5=x1+x2;
  298.   temperature=(b5+8)>>4;      
  299.   conversion(temperature);    //调用温度显示转换函数
  300.   DisplayOneChar(5,0,bai);    //显示温度十位   
  301.   DisplayOneChar(6,0,shi);    //显示温度个位
  302.   DisplayOneChar(8,0,ge);     //显示温度小数后一位

  303.   BMP180ReadPressure();           //读取气压
  304.   up=ReadPressure[0]<<8|ReadPressure[1];  //合成气压数据
  305.   up&=0x0000FFFF;
  306.   b6=b5-4000;                     //以下根据EEPROM中的值对获取的气压数据的进行补偿换算
  307.   x1=(b2*(b6*b6>>12))>>11;
  308.   x2=ac2*b6>>11;
  309.   x3=x1+x2;
  310.   b3=(((long)ac1*4+x3)+2)/4;
  311.   x1=ac3*b6>>13;
  312.   x2=(b1*(b6*b6>>12))>>16;
  313.   x3=((x1+x2)+2)>>2;
  314.   b4=(ac4*(unsigned long)(x3+32768))>>15;
  315.   b7=((unsigned long)up-b3)*(50000>>0);
  316.   if(b7<0x80000000)
  317.     p=(b7*2)/b4;
  318.   else  
  319.     p=(b7/b4)*2;
  320.   x1=(p>>8)*(p>>8);
  321.   x1=(x1*3038)>>16;
  322.   x2=(-7357*p)>>16;
  323.   pressure=p+((x1+x2+3791)>>4);
  324.   conversion(pressure);           //调用气压显示转换函数
  325.   DisplayOneChar(4,1,shiwan);     //显示气压的百位
  326.   DisplayOneChar(5,1,wan);        //显示气压的十位
  327.   DisplayOneChar(6,1,qian);       //显示气压的个位
  328.   DisplayOneChar(8,1,bai);        //显示气压小数后一位
  329.   DisplayOneChar(9,1,shi);        //显示气压小数后二位
  330. }
  331. //==================主函数=====================
  332. void main(void)   
  333. {
  334.   delay_nms(400);                    //延时400ms等待电源稳定
  335.   DDRA=0xff;PORTA=0x00;
  336.   DDRB=0xff;PORTB=0x00;
  337.   DDRC=0xff;PORTC=0xff;
  338.   DDRD=0xff;PORTD=0xff;         //初始化I/O口
  339.   InitLcd();                                 //LCD初始化
  340.   Init_BMP180();            //BMP180初始化
  341.   ePutstr(0,0,str0);        //显示温度
  342.   delay_nms(10);
  343.   DisplayOneChar(10,0,0xdf);  //显示特殊符号
  344.   delay_nms(10);
  345.   ePutstr(0,1,str1);        //显示气压
  346.   while(1)
  347.   {
  348.     BMP180Convert();        //调用转换
  349.     delay_nms(1000);
  350.   }
  351. }
复制代码
上述程序中,是运用延时来等待数据转换完成的,并没使用EOC脚来检查转化是否完毕。同时,程序利用EEPROM中的值对获取的数据进行补偿换算的方法是直接来自BMP180的数据手册,若对换算代码有问题可参看其手册。气压数据采用了常规的16位数据,并没有扩展到19位。另外,由于在程序中大量使用了长整型数据格式,所以在IAR开发环境中编译时仍然要把编译优化选项改成Low或Medium(具体参见第一章),系统才能正常运行。若选择不优化(None)时,可能在液晶屏得不到任何显示。究其原因可能与IAR的编译环境有关,这一点要非常注意!!

把程序下载到单片机中,按要求接好连线,给系统上电,就可以在液晶屏上看到实时的温度和气压数据了

以上的Word格式文档51黑下载地址:
BMP180中文资料.doc (166.5 KB, 下载次数: 10)

评分

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

查看全部评分

回复

使用道具 举报

柚zi茶 发表于 2020-4-25 21:34 | 显示全部楼层
谢谢  正需要这个呐
回复

使用道具 举报

abc123-def 发表于 2020-4-27 12:12 来自手机 | 显示全部楼层
很好,谢谢楼主
回复

使用道具 举报

freeboyxd 发表于 2020-5-13 21:47 | 显示全部楼层
谢谢楼主的无私分享
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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