<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>嵌入式 :: yafeng 的博客</title><link>http://yafengabc.github.io/embedded/index.html</link><description>树莓派与嵌入式折腾记录：wiringPi GPIO 库系列、MicroPython 入坑系列、RP2040 / 串口上位机、INA226 电量监测、Arduino / ESP 开发板对比等，按系列分组排列，陆续从博客园归档迁入。&#10;树莓派读取DHT11传感器的源代码 树莓派高级GPIO库，wiringpi2 for python使用笔记（一）安装 树莓派高级GPIO库，wiringpi2 for python使用笔记（二）高精度计时、延时函数 树莓派高级GPIO库，wiringpi2 for python使用笔记（三）GPIO操作 树莓派高级GPIO库，wiringpi2 for python使用笔记（四）实战DHT11解码 树莓派高级GPIO库，wiringpi2 for python使用笔记（五）i2c读取测试 树莓派提高实时性的新思路[原创] 简单的实现树莓派的WEB控制 让普通用户可以控制树莓派的GPIO(Archlinuxarm) intel Z8300 2G 32G小主机跟树莓派3B的功耗/性能对比 MicroPython入坑记（一）（ESP8266 ESP32） MicroPython入坑记（二）刷固件（ESP8266 ESP32） MicroPython入坑记（三）板子上的Python到底有多快？ MicroPython入坑记（四）关于MicroPython的代码保护 MicroPython入坑记(四)利用MQTT实现0编程远程控制（上）（用安卓手机） MicroPython入坑记(四)利用MQTT实现0编程远程控制（下）（用安卓手机） MicroPython与Python速度对比 RP2040踩坑记 USB CDC串口无法下载程序(驱动)+不发送数据（DTR） Windows下串口高速接收数据的一些问题。 串口助手极限测试 INA226折腾笔记（一） INA226折腾笔记（二） Arduino UNO ESP8266 ESP32 MicroBit IO操作速度对比记录</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="http://yafengabc.github.io/embedded/index.xml" rel="self" type="application/rss+xml"/><item><title>树莓派读取DHT11传感器的源代码</title><link>http://yafengabc.github.io/embedded/p5096184/index.html</link><pubDate>Sun, 03 Jan 2016 13:27:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p5096184/index.html</guid><description>import wiringpi2 as gpio owpin=8 #第8脚为1-wire脚 def getval(owpin): tl=[] #存放每个数据位的时间 tb=[] #存放数据位 gpio.wiringPiSetup() #初始化wiringpi库 gpio.pinMode(owpin,1) #设置针脚为输出状态 gpio.digitalWrite(owpin,1) #输出高电平 gpio.delay(1) gpio.digitalWrite(owpin,0) #拉低20ms开始指令 gpio.delay(25) gpio.digitalWrite(owpin,1) #抬高20-40us gpio.delayMicroseconds(20) gpio.pinMode(owpin,0) #设针脚为输入状态 while(gpio.digitalRead(owpin)==1): pass #等待DHT11拉低管脚 for i in range(45): #测试每个数据周期的时间（包括40bit数据加一个发送开始标志 tc=gpio.micros() #记下当前us数（从初始化开始算起，必要时重新初始化） ''' 一个数据周期，包括一个低电平，一个高电平，从DHT11第一次拉低信号线开始 到DHT11发送最后一个50us的低电平结束（然后被拉高，一直维持高电平，所以 最后的完成标志是一直为高，超过500ms） ''' while(gpio.digitalRead(owpin)==0):pass while(gpio.digitalRead(owpin)==1): if gpio.micros()-tc&gt;500: #如果超过500ms就结束了 break if gpio.micros()-tc&gt;500: #跳出整个循环 break tl.append(gpio.micros()-tc) #记录每个周期时间的us数，存到tl这个列表 # print(tl) #反注释后可打印时间列表 tl=tl[1:] #去掉第一项，剩下40个数据位 for i in tl: if i&gt;100: #若数据位为1，时间为50us低电平+70us高电平=120us tb.append(1) else: tb.append(0) #若数据位为0，时间为50us低电平+25us高电平=75us #这里取大于100us就为1 # print(tb) #反注释可查看每一位状态 return tb def GetResult(owpin): for i in range(10): SH=0;SL=0;TH=0;TL=0;C=0 result=getval(owpin) # print(len(result)) if len(result)==40: for i in range(8): #计算每一位的状态，每个字8位，以此为湿度整数，湿度小数，温度整数，温度小数，校验和 SH*=2;SH+=result[i] SL*=2;SL+=result[i+8] TH*=2;TH+=result[i+16] TL*=2;TL+=result[i+24] C*=2;C+=result[i+32] if ((SH+SL+TH+TL)%256)==C and C!=0: break else: print("Read Sucess,But checksum error! retrying") else: print("Read failer! Retrying") gpio.delay(200) return SH,SL,TH,TL SH,SL,TH,TL=GetResult(owpin) print("湿度:",SH,SL,"温度:",TH,TL)</description></item><item><title>树莓派高级GPIO库，wiringpi2 for python使用笔记（一）安装</title><link>http://yafengabc.github.io/embedded/p5096300/index.html</link><pubDate>Sun, 03 Jan 2016 14:06:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p5096300/index.html</guid><description>网上的教程，一般Python用RPi.GPIO来控制树莓派的GPIO，而C/C++一般用wringpi库来操作GPIO，RPi.GPIO过于简单，很多高级功能不支持，比如i2c/SPI库等，也缺乏高精度定时等高级特性。相比之下，wiringpi则功能丰富的多，其实wringpi已经有了python绑定，可以非常简单的在python中使用这个库。鉴于网上基本没有这个库的中文说明，我一边学习，一边以做笔记的形式，写几篇关于这个库的基本使用的文章。&#10;安装：首先安装python-pip：&#10;我用的Archlinux，python3，安装命令为：&#10;pacman -S python-pip 如果用python2，安装命令为：&#10;pacman -S python2-pip Raspbian下则为：&#10;apt-get install python3-pip apt-get install python-pip 安装完后，就可以用pip install来安装python库了。为避免繁琐，我下边的命令都以pip命令安装，Archlinux下默认为python3的pip3，如果使用个python2则用pip2来代替pip，debian下pip默认为pip2，若使用python3，则使用pip3来代替。&#10;pip install wiringpi2 pip库里除了wiringpi2外，还有老版本的wiringpi库，大家按需安装。&#10;安装完后，运行pip list，可以看到列表中包含了新装的wringpi2库了：&#10;在终端中敲入python，进入python控制台，导入一下，如果不报错，说明安装成功：</description></item><item><title>树莓派高级GPIO库，wiringpi2 for python使用笔记（二）高精度计时、延时函数</title><link>http://yafengabc.github.io/embedded/p5096445/index.html</link><pubDate>Sun, 03 Jan 2016 15:11:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p5096445/index.html</guid><description>学过单片机的同学应该清楚，我们在编写传感器驱动时，需要用到高精度的定时器、延时等功能，wiringpi提供了一组函数来实现这些功能，这些函数分别是：&#10;micros() #返回当前的微秒数，这个数在调用wiringPiSetup()后被清零并重新计时&#10;millis() #返回当前的毫秒数，同上，这个数在调用wiringPiSetup()后被清零并重新计时&#10;delayMicroseconds() #高精度微秒延时&#10;delay() #毫秒延时。&#10;python相对于C，一个很大的问题就是执行速度慢，所以指令执行速度不可忽视，我们可以用micos函数来检测指令执行时间，用来避免实际使用中遇到的坑，请看以下代码：&#10;import wiringpi2 as gpio for i in range(5): t1=gpio.micros() t2=gpio.micros() print(t2-t1) 连续调用两次micros，然后打印出差值，运行结果如下：&#10;[root@RasPi ~/testcode]# python testus.py 12 4 4 5 5 我们看到第一次的结果明显比以后的结果要大，多了接近10微秒，一般的程序来说，这无关紧要，要是要求更高，可以把代码改成这个样子:&#10;import wiringpi2 as gpio for i in range(5): t1=gpio.micros() t1=gpio.micros() t2=gpio.micros() print(t2-t1) 运行结果如下：&#10;[root@RasPi ~/testcode]# python testus.py 3 3 3 3 2 基本一致了再看以下代码：&#10;import wiringpi2 as gpio for i in range(5): t1=gpio.micros() t1=gpio.micros() gpio.delayMicroseconds(10) t2=gpio.micros() print(t2-t1) 延时10us，结果如下：&#10;[root@RasPi ~/testcode]# python testus.py 21 21 18 18 18 减去两次调用micros()之间的5us左右的延时，实际延时10us会有5us左右的延时。</description></item><item><title>树莓派高级GPIO库，wiringpi2 for python使用笔记（三）GPIO操作</title><link>http://yafengabc.github.io/embedded/p5096720/index.html</link><pubDate>Sun, 03 Jan 2016 16:57:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p5096720/index.html</guid><description>GPIO库的核心功能，当然就是操作GPIO了，GPIO就是“通用输入/输出”接口，比如点亮一个LED、继电器等，或者通过iic spi 1-wire等协议，读取、写入数据，这都是GPIO的用处，可以说没有GPIO，树莓派只能当小电脑用，有了GPIO，就升级成一个控制器了。先来说说怎么操作一个数字量（高低电平）。&#10;先看代码：&#10;import wiringpi2 as gpio from wiringpi2 import GPIO gpio.wiringPiSetup() #初始化 gpio.pinMode(25,GPIO.OUTPUT) # 把pin25设置为输出模式 gpio.digitalWrite(25,GPIO.HIGH) #pin25输出为高电平 print(gpio.digitalRead(25)) #打印pin25的状态 值的注意的是，GPIO在输出模式时，也可以读取GPIO状态。&#10;wiringpi对树莓派2的GPIO定义如下：&#10;[root@RasPi ~/testcode]# gpio readall +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | | | 3.3v | | | 1 || 2 | | | 5v | | | | 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5V | | | | 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | | | 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT0 | TxD | 15 | 14 | | | | 0v | | | 9 || 10 | 1 | ALT0 | RxD | 16 | 15 | | 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 | | 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | | | 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 | | | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 | | 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | | | 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | | 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 | | | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 | | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | | 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | | | 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 | | 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | | | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | | | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+ 我们看到，wringpi对针脚有三种定义方式，BCM代表GPIO模式，wPi代表pin模式，Physical代表物理针脚模式。</description></item><item><title>树莓派高级GPIO库，wiringpi2 for python使用笔记（四）实战DHT11解码</title><link>http://yafengabc.github.io/embedded/p5100741/index.html</link><pubDate>Tue, 05 Jan 2016 00:12:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p5100741/index.html</guid><description>DHT11是一款有已校准数字信号输出的温湿度传感器。 精度湿度+-5%RH， 温度+-2℃，量程湿度20-90%RH， 温度0~50℃。&#10;我买的封装好的模块，上边自带了上拉电阻，直接查到树莓派上即可灰、紫、蓝分别代表数据、3.3V、0V,接到树莓派的3，1，10脚，分别对应PIN8，3.3V，0V。&#10;DHT11与单片机通讯协议为单线协议（1-wire），其实单线协议蛮厉害的，一个GPIO就能实现数据的读取，但是这个协议没有同步脉冲，所以对时序要求比较高，比如DHT11对高低电平定义如下：&#10;低电平50us，然后一个26-28us的高电平，代表0&#10;低电平50us，然后一个70us的高电平，代表1&#10;也就是说，需要能分辨出40us以下的时间才能准确的测出，下边看看具体的时序：&#10;总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。&#10;数字0表示如下图：&#10;数字1表示如下图：&#10;可以看出，每一位包括一开始的响应信号，都是由一个低电平跟一个高电平组成，其中响应信号为80us+80us=160us&#10;数字0为50+26=76us&#10;数字1为50+70=120us&#10;为读到DHT11的状态，我编写了以下的程序：&#10;import wiringpi2 as gpio owpin=8 #第8脚为1-wire脚 tl=[] #存放每个数据位的时间 gpio.wiringPiSetup() #初始化wiringpi库 gpio.pinMode(owpin,1) #设置针脚为输出状态 gpio.digitalWrite(owpin,1) #输出高电平 gpio.delay(1) ###发开始指令，要求DHT11传输数据 gpio.digitalWrite(owpin,0) #拉低25ms开始指令 gpio.delay(25) gpio.digitalWrite(owpin,1) #输出高电平，开始指令结束 gpio.pinMode(owpin,0) #设针脚为输入状态 ###开始指令发送完毕，把管脚设置为高电平，并等待DHT11拉低管脚。传输数据 while(gpio.digitalRead(owpin)==1): pass #如果管脚一直是1，则一直等待。 ###若被拉低，说明传输开始，应答信号+40位数据+结束标志共42位 ###下边共循环45次，故意多循环几次看结果。 for i in range(45): #测试每个数据周期的时间（包括40bit数据加一个发送开始标志 tc=gpio.micros() #记下当前us数（从初始化开始算起，必要时重新初始化） ''' 一个数据周期，包括一个低电平，一个高电平，从DHT11第一次拉低信号线开始 到DHT11发送最后一个50us的低电平结束（然后被拉高，一直维持高电平，所以 最后的完成标志是一直为高，超过500ms） ''' while(gpio.digitalRead(owpin)==0):pass #一位数据由一个低电平 while(gpio.digitalRead(owpin)==1): #加一个高电平组成 if gpio.micros()-tc&gt;500: #如果超过500us就结束了本次循环,传输结束后 break #会被上拉电阻拉成高电平，防止进入死循环 tl.append(gpio.micros()-tc) #记录每个周期时间的us数，存到tl这个列表 print(tl) #打印结果 程序里有详细的解释，我就不再赘述，这里贴出我这里的执行结果：</description></item><item><title>树莓派高级GPIO库，wiringpi2 for python使用笔记（五）i2c读取测试</title><link>http://yafengabc.github.io/embedded/p5107768/index.html</link><pubDate>Wed, 06 Jan 2016 22:52:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p5107768/index.html</guid><description>wiringpi2显然也把i2c驱动带给了Python，手头上正巧有一个DS3231的模块，上边带了一个DS3231 RTC（实时时钟），与一片24C32，两个芯片均为iic总线设备，与树莓派接线如下：&#10;也就是VCC GND SDA SCL四个脚分别接到树莓派的1（3.3v）、9（0v）、3（SDA.1）、5（SCL.1）上，因为树莓派的I2C接口默认是关闭的，需要先编辑一下/boot/config.txt,去掉 device_tree_param=i2c_arm=on上的注释（ArchlinuxARM RasperryPi2），然后重启（注：Raspbian可以用raspi-config打开）&#10;然后重启，重启完成后，运行&#10;modprobe i2c-dev 若想这个模块自动装载，请把它写到 /etc/modules-load.d/raspberrypi.conf&#10;安装i2c-tools，Archlinux下为：&#10;pacman –S i2c-tools 安装后，运行i2cdetect –y 1结果如下：&#10;嗯，发现了57，68两个设备，哪个是DS3231，哪个又是24C32呢，我们把里边的数据dump出来看看：&#10;可以看到0x57设备里边是空的，应该就是24C32了，0x68里边读出来20个字节，就是DS3231了。&#10;我先解释下这几个命令：&#10;i2cdetect顾名思义就是搜索i2c总线的设备，树莓派有2条i2c总线，咱们接的SDA.1,SCL.1，当然就是搜索1这条总线了（另外一条是SDA.0 SCL.0）&#10;-y参数没啥意义，就是自己帮你按下y(yes).&#10;i2cdump也很容易理解，就是dump出指定总线，指定设备的数据这里是1总线0x57 0x68两个设备。-y参数跟上个命令是一样的。&#10;这样，我们的i2c设备就都通讯上了，下边就是用wiringpi2库读写之。&#10;wringpi中操作i2c设备的函数主要有一下几个：</description></item><item><title>树莓派提高实时性的新思路[原创]</title><link>http://yafengabc.github.io/embedded/p6292265/index.html</link><pubDate>Tue, 17 Jan 2017 11:15:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p6292265/index.html</guid><description>树莓派提高实时性的新思路&#10;by yafeng 转载请注明&#10;树莓派是一台应用linux系统的ARM电脑，由于linux是非实时性系统，所以各个任务之间会不停的切换，时间片20ms-100ms不等，所以很难获得实时的输出，这里先贴个小例子，内容很简单，把一个IO点置为高电平，等待1000us（1ms）然后置为低电平，并记录需要的时间，记录10000次（2ms*10000=20秒）。&#10;#include "stdio.h" #include "wiringPi.h" #define rec_num 10000 int main(){ int time_arry[rec_num]; int current_time=0; wiringPiSetup(); pinMode(0,OUTPUT); for(int i=0;i&lt;rec_num;i++){ current_time=micros(); digitalWrite(0,HIGH); delayMicroseconds(1000); digitalWrite(0,LOW); delayMicroseconds(1000); time_arry[i]=micros()-current_time; } for(int j=0;j&lt;rec_num;j++){ printf("%d ",time_arry[j]); } } 代码很简单，最后打印10000次时间结果，单位为微秒,部分结果如下：&#10;$ gcc set_io_pluse_nort.c -lwiringPi $ sudo ./a.out 2189 2127 2127 2128 2127 2126 2125 2125 2128 2126 2125 2126 2126 2128 2126 2126 2125 2133 2134 2133 2132 2132 2134 2132 2132 2133 2132 2134 2133 2132 2132 2133 2132 2133 2132 2132 2135 2132 2132 2132 2132 2133 2131 2132 2133 2137 2133 2132 2132 2131 2133 2132 2132 2132 2132 2133 2132 2132 2132 2137 2126 2126 2125 2126 2132 2133 2132 2132 2132 2133 2134 2133 2132 2132 2133 2132 2132 2132 2133 2132 2132 2132 2132 2133 2132 2132 2131 2132 2135 2126 2126 2125 2130 2132 2131 2131 2131 2133 2131 2131 2131 2132 2138 2131 2132 2131 2132 2132 2131 2131 2131 2132 2131 2133 2132 2131 2128 2126 2125 2125 2129 2128 2127 2126 2131 2129 2126 2125 2125 2126 2129 2126 2126 2126 2125 2128 2126 2126 2126 2128 2127 2126 2126 2125 2134 2155 2140 2139 2138 2138 2138 2138 2137 2137 2139 2137 2136 2136 21 由于数据太多，不容易看出结果，所以我用Python写了个统计结果的程序，分析均值以及偏差。代码很简单，我就不贴出来了，只贴结果：&#10;平均时间: 2132 &lt;10us: 9621 &gt;10us: 282 &gt;20us: 49 &gt;30us: 19 &gt;40us: 7 &gt;50us: 4 &gt;60us: 2 &gt;70us: 2 &gt;80us: 2 &gt;90us: 2 &gt;100us: 2 &gt;110us: 2 &gt;120us: [2488, 2346] 可以看出，现在结果还是不错的，只有2个结果误差超过均值60us，但是平均时间是2.132ms，跟预计时间有132us的误差。&#10;下边是该程序在CPU满载的情况下的测试结果（我写了个多进程程序，把所有CPU撑到100%）：&#10;平均时间: 2128 &lt;10us: 73 &gt;10us: 9902 &gt;20us: 175 &gt;30us: 153 &gt;40us: 71 &gt;50us: 24 &gt;60us: 15 &gt;70us: 15 &gt;80us: 15 &gt;90us: 15 &gt;100us: 15 &gt;110us: 15 &gt;120us: [12448, 5813, 9480, 13708, 7936, 11588, 14554, 10433, 13771, 14565, 12347, 5887, 9486, 13725, 2955] 这时，出现了非常差的情况，15次误差大于120us，并且有多次10-15ms误差的！</description></item><item><title>简单的实现树莓派的WEB控制</title><link>http://yafengabc.github.io/embedded/p5197844/index.html</link><pubDate>Thu, 18 Feb 2016 12:55:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p5197844/index.html</guid><description>最终效果如图：&#10;用到的知识：Python Bottle HTML Javascript JQuery Bootstrap AJAX 当然还有 linux&#10;我去，这么多……我还是一点一点说起吧……&#10;先贴最终的源代码：&#10;#!/usr/bin/env python3 from bottle import get,post,run,request,template @get("/") def index(): return template("index") @post("/cmd") def cmd(): print("按下了按钮: "+request.body.read().decode()) return "OK" run(host="0.0.0.0") 没错，就10句，我一句一句解释：&#10;1.#!/usr/bin/env python3 ，告诉shell这个文件是Python源代码，让bash调用python3来解释这段代码&#10;2.from bottle import get,post,run,request,template ，从bottle框架导入了我用到的方法、对象&#10;下边几句是定义了2个路由，一个是“/”一个是“/cmd”,前者是get类型（用@get装饰），后者是POST类型（用的@post装饰）&#10;第一个路由很简单，就是读取index模版（模版就是个html啦）并发送到客户端（浏览器），因为路径是“/”也就是比如树莓派的IP地址是：192.168.0.10&#10;那用http://192.168.0.10:8080就访问到了我们的"/”路由（bottle默认端口是8080）&#10;同理，第二个路由的路径是“/cmd”也就是访问http://192.168.0.10:8080/cmd就访问到了第二个路由&#10;最后一句：run(host=“0.0.0.0”)就是调用bottle的run方法，建立一个http服务器，让我们能通过浏览器访问我们的界面。&#10;下边我详细的解释一下这些代码的作用：&#10;第一个路由的作用就是扔给浏览器一个HTML（index.tpl）文档，显示这个界面：&#10;这个文件的源代码如下：&#10;&lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="UTF-8"&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt; &lt;title&gt;遥控树莓派&lt;/title&gt; &lt;link href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" media="screen"&gt; &lt;script src="http://code.jquery.com/jquery.js"&gt;&lt;/script&gt; &lt;style type="text/css"&gt; #up { margin-left: 55px; margin-bottom: 3px; } #down { margin-top: 3px; margin-left: 55px; } &lt;/style&gt; &lt;script&gt; $(function(){ $("button").click(function(){ $.post("/cmd",this.id,function(data,status){}); }); }); &lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="container" class="container"&gt; &lt;div&gt; &lt;button id="up" class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-up"&gt;&lt;/button&gt; &lt;/div&gt; &lt;div&gt; &lt;button id='left' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-left"&gt;&lt;/button&gt; &lt;button id='stop' class="btn btn-lg btn-primary glyphicon glyphicon-stop"&gt;&lt;/button&gt; &lt;button id='right' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-right"&gt;&lt;/button&gt; &lt;/div&gt; &lt;div&gt; &lt;button id='down' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-down"&gt;&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; 这个内容有点多，不过很简单，就是引用了jquery bootstrap这两个前端框架，加了5个按钮(之间的代码)。当然我用了bootstrap内置的上下左右停止这几个图标，这5个按钮的id分辨定义成up，down，left，right，stop，然后写了如下的关键代码：</description></item><item><title>让普通用户可以控制树莓派的GPIO(Archlinuxarm)</title><link>http://yafengabc.github.io/embedded/p8873397/index.html</link><pubDate>Wed, 18 Apr 2018 11:52:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p8873397/index.html</guid><description>Raspbian上的Rpi.GPIO库是可以在普通用户下控制树莓派的GPIO的，然而在ArchlinuxARM下，却需要root用户才行，这无疑会引起安全问题，好在RPi.GPIO提供了一个获取权限的脚本：create_gpio_user_permissions.py&#10;然而这个脚本是没法在ArchlinuxARM上直接运行的，因为Archlinux缺少adduser命令。&#10;这个脚本是这样的：&#10;import grp import subprocess def ensure_gpiogroup(): try: grp.getgrnam('gpio') except KeyError: print('GPIO group does not exist - creating...') subprocess.call(['groupadd', '-f', '-r', 'gpio']) subprocess.call(['adduser', 'pi', 'gpio']) # in future, also for groups: # spi # i2c add_udev_rules() def add_udev_rules(): with open('/etc/udev/rules.d/99-gpio.rules','w') as f: f.write("""SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660" SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'" SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'" """) if __name__ == '__main__': ensure_gpiogroup() 反正很简单，手动搞进去就行了</description></item><item><title>intel Z8300 2G 32G小主机跟树莓派3B的功耗/性能对比</title><link>http://yafengabc.github.io/embedded/p11562232/index.html</link><pubDate>Sat, 21 Sep 2019 11:52:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p11562232/index.html</guid><description>具体硬件平台就不详细说了，浪费时间码字，贴一个淘宝上的配置：&#10;有点广告的既视感，哈哈，不过我200块咸鱼收的2手，配置比这个稍低，比如CPU是Z8300，网卡是百兆的，WIFI可能也不是双频的（这个不确定，因为linux下这玩意没驱动起来）不过我买回来不是做NAS，只是想搞个7*24在线的小服务器，映射到外网做一点小服务。所以配置差点也无所谓的样子&#10;先来个待机功耗图：&#10;待机2.0瓦。不过这个是算上这个220V/5V 3A的效率的，因为电源的总功率是15W(5V/3A)而目标功率小于3W，所以也算是低负载运行，所以实际功耗应该是小于2瓦的，不像直测USB电流，测出来就是实际功耗，所以在接下来的对比中，我树莓派也用一个5V 3A的充电头来带，尽量做到情景一致&#10;（不过考虑到不同的充电头，效率也不一样，所以还是不算严谨对比）先来个screenfetch。&#10;-` .o+` yafeng@archlinux `ooo/ OS: Arch Linux `+oooo: Kernel: x86_64 Linux 5.3.0-arch1-1-ARCH `+oooooo: Uptime: 1h 5m -+oooooo+: Packages: 188 `/:-:++oooo+: Shell: bash 5.0.9 `/++++/+++++++: CPU: Intel Atom x5-Z8300 @ 4x 1.84GHz [40.2°C] `/++++++++++++++: GPU: `/+++ooooooooooooo/` RAM: 88MiB / 1862MiB ./ooosssso++osssssso+` .oossssso-````/ossssss+` -osssssso. :ssssssso. :osssssss/ osssso+++. /ossssssss/ +ssssooo/- `/ossssso+/:- -:/+osssso+- `+sso+:-` `.-/+oso: `++:. `-/+/ .` `/ 算了，不说废话了，先记下现在测试过的结果：</description></item><item><title>MicroPython入坑记（一）（ESP8266 ESP32）</title><link>http://yafengabc.github.io/embedded/p8680938/index.html</link><pubDate>Sat, 31 Mar 2018 08:55:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p8680938/index.html</guid><description>手上有块基于ESP8266的NodeMCU板子，没错，就是那个10块钱的带WIFI的芯片，当时感觉又便宜又又有wifi，并且可以用脚本lua写代码，果断买买买，然后玩了一下WIFI跟树莓派通讯（树莓派开Http Server，然后用lua通过socket模拟http指令定期获取树莓派的指令）。控制了个继电器加灯泡玩了玩，手头上的dht11也能直接读出来（nodemcu内置驱动了），然后就没有然后了，感觉lua不是我的菜，用惯了python，写lua还是十分别扭的.嗯对lua的吐槽开始：&#10;1.没有高层次的封装，缺少轮子，虽然有cjson，socket等可用，然而没有httpclient之类更高层次的封装，搞个rest 请求还得自己写http头，虽然也不麻烦，然而用惯了urllib的python用户自然不是很爽。lua本身是作为C的嵌语言设计的，所以基本库太有限，虽然网上配合lua的C库众多，然而想搞进nodemcu的固件里，我这半吊子水平还是有难度的，一句话就是nodemcu提供的功能，可以很好的搞，没提供的功能干瞪眼。&#10;2.一些语法上的吐槽，比如默认全局变量，局部变量要加local之类的，年代久远，记不清了总之用惯了python习惯了优雅的设计，就不习惯丑陋了（引战了引战了）&#10;3.固件开发不活跃，现在github上的代码基本都是几年前的了，虽然最新的2.1.0发布于2017年8月，看看changelog，其实就是一个18B20驱动的重写，没有什么大改进。这也是国人类似项目的一贯作风：为了卖板子然后找个大神移植下固件，然后能把东西卖出去就OK了，至于固件的演进，看大神的的心情了……&#10;4.nodemcu只能用在esp8266上，当然esp32也可以跑了，然而也就这样了，参照上边那条，开发不活跃啊，不像micropython或者espruino（一个跑在单片机的javascript固件），官方就支持好多的MCU&#10;所以后来有了Arduino for ESP8266，就用Arduino IDE耍去了，那个支持的MCU多(lll￢ω￢)，以前其实也知道有MicroPython，但大神说因为8266资源有限，被裁减的太厉害（其实当然完全不是那么回事）啥也干不了，不如等ESP32（然后我就真等ESP32了）。&#10;经过漫长的等待，ESP32的板子终于价格降到心理价位，然后果断入坑，当然是刷MicroPython了，然后为了对比，顺便把手上的8266也刷上了MicroPython固件，搞起来，先看可用内存：&#10;ESP8266：&#10;micropython.mem_info()&#10;stack: 2112 out of 8192&#10;GC: total: 35968, used: 9376, free: 26592&#10;No. of 1-blocks: 50, 2-blocks: 10, max blk sz: 264, max free sz: 1261&#10;嗯，8K的的栈，36K的堆确实小了些，然而……esp32也好不到哪儿去&#10;micropython.mem_info()&#10;stack: 736 out of 15360&#10;GC: total: 111168, used: 6864, free: 104304&#10;No. of 1-blocks: 26, 2-blocks: 8, max blk sz: 264, max free sz: 6402&#10;纳尼？说好的400+Kb的内存呢，怎么只有110K左右？（虽然比8266是好多了）&#10;经过探索gayhub上的代码，终于明白了原因：</description></item><item><title>MicroPython入坑记（二）刷固件（ESP8266 ESP32）</title><link>http://yafengabc.github.io/embedded/p8681380/index.html</link><pubDate>Sat, 31 Mar 2018 11:13:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p8681380/index.html</guid><description>先来个刷写过程：&#10;其实各种刷ESP8266/ESP32的软件都可以搞（比如刷NodeMCU的软件），不过我这还是出问题了：刷完后无限输出乱码。然后参照Micropython官方的教程，用esptool先清除所有的FLASH，然后重新刷入，正常运行：&#10;pip install esptool 安装esptool（当然，电脑上要先装好python） esptool.py --port /dev/ttyUSB0 erase_flash 擦除所有的flash，如果是windows系统，则改为具体的COM口，比如我的电脑是COM4： esptool.py --port COM4 erase_flash刷入固件这个命令比较长，可以不熟悉命令行的可以用其他GUI软件代替： esptool.py --port COM4 --baud 460800 write_flash --flash_size=detect 0 esp8266.bin（这个改为具体固件文件名） 刷完后，用putty连接板子会出现以下界面（如果没有显示就先按一下RST键）： 现在就可以执行Python代码了，支持命令补全哦列出所有的模块：继续探索：在ESP8266上，跟板子相关的代码主要在esp machine两个模块上： import esp&#10;import machine&#10;dir(esp)&#10;[’__class__’, ‘__name__’, ‘SLEEP_LIGHT’, ‘SLEEP_MODEM’, ‘SLEEP_NONE’, ‘apa102_write’, ‘check_fw’, ‘deepsleep’, ‘dht_readinto’, ’esf_free_bufs’, ‘flash_erase’, ‘flash_id’, ‘flash_read’, ‘flash_size’, ‘flash_user_start’, ‘flash_write’, ‘free’, ‘freemem’, ‘info’, ‘malloc’, ‘meminfo’, ’neopixel_write’, ‘osdebug’, ‘set_native_code_location’, ‘sleep_type’]&#10;dir(machine)&#10;[’__class__’, ‘__name__’, ‘ADC’, ‘DEEPSLEEP’, ‘DEEPSLEEP_RESET’, ‘HARD_RESET’, ‘I2C’, ‘PWM’, ‘PWRON_RESET’, ‘Pin’, ‘RTC’, ‘SOFT_RESET’, ‘SPI’, ‘Signal’, ‘Timer’, ‘UART’, ‘WDT’, ‘WDT_RESET’, ‘deepsleep’, ‘disable_irq’, ’enable_irq’, ‘freq’, ‘idle’, ‘mem16’, ‘mem32’, ‘mem8’, ‘reset’, ‘reset_cause’, ‘sleep’, ’time_pulse_us’, ‘unique_id’]</description></item><item><title>MicroPython入坑记（三）板子上的Python到底有多快？</title><link>http://yafengabc.github.io/embedded/p8681713/index.html</link><pubDate>Sat, 31 Mar 2018 12:26:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p8681713/index.html</guid><description>前几篇都是直接在命令上操作，然而执行py文件才是王道，其实类似nodemcu的lua固件，MicroPython也提供了一个简单的文件系统用来存代码。并自动在开机时执行：&#10;boot.py&#10;main,py&#10;两个文件&#10;windows用户的话，推荐一个软件：uPyLoader界面如下&#10;可以看到，左边是本地的文件，右边是ESP8266的文件，两边可以互相传输，也可以删除上边的文件，其他功能自行探索，这个软件自带了一个编辑器，不过很弱。&#10;还是推荐用vim或者vscode来写代码，然后用这个软件把代码传进去。&#10;测试代码么，先来个循环：&#10;import time import micropython def loop1(): t1=t2=0 for i in range(5): t1=time.ticks_us() for i in range(100): pass t2=time.ticks_us() print(t2-t1) time.sleep(2) loop1() 默认工作在80MHZ下，结果是：&#10;=== with open("test_loop.py") as f: === exec(f.read(), globals()) === 4715 4695 4742 4698 4697 &gt;&gt;&gt; 脚本确实不快，100次循环足足用了4.5ms，即使把速度调成160MHZ，也就2.5ms左右也就一次循环25us左右，而ESP32下速度就很赞了：&#10;paste mode; Ctrl-C to cancel, Ctrl-D to finish === with open("test_loop.py") as f: === exec(f.read(), globals()) === 346 262 274 267 278 &gt;&gt;&gt; 10多倍的提速2us-3us循环一次，甚至可以写dht11那样的时序驱动了。esp8266就没办法了吗？当然有!&#10;MicroPython官方文档上专门有篇代码提速的文章，其中提到了可以用装饰器micropython.native micropython.viper来给代码打鸡血（编译成机器码）&#10;import time import micropython @micropython.native def loop1(): t1=t2=0 for i in range(5): t1=time.ticks_us() for i in range(100): pass t2=time.ticks_us() print(t2-t1) time.sleep(2) loop1() 加了个@micropython.native装饰器，速度立马鸡血：</description></item><item><title>MicroPython入坑记（四）关于MicroPython的代码保护</title><link>http://yafengabc.github.io/embedded/p8682910/index.html</link><pubDate>Sat, 31 Mar 2018 16:47:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p8682910/index.html</guid><description>脚本开发东西，可能面临的第一个问题就是：拷给别人，代码怎么写的他不就都知道了？不行，我要保住我的小秘密！&#10;先说下结果：没有攻不破的堡垒，即使你写成C语言，只要能拿到二进制结果，都可以反汇编逆向出你是怎么实现的，关键是值不值得&#10;另外，这跟逆向者对系统的了解程度有关，比如对方连代码都不会上传，那你即使把源文件放进去也他也无可奈何。&#10;好了，言归正传，我们知道普通python有个编译成字节码的功能，也就是源代码会在解释时先编译成一个类似java中间代码的结果，这是不可读的（但是这也不排除反编译的可能，毕竟JAVA的class文件是有反编译软件的）。&#10;micropython也有这个功能，不过这个文件的扩展名是mpy，也不能在运行时自动生成，需要一款软件：mpy-cross.exe，这是micropython官方提供的，可以用python pip直接安装，安装完成后，可以运行mpy-cross.exe pythonfile,py，就可以生成一个pythonfile.mpy的字节码文件，这文件使用跟py文件是等效的。把所有文件生成mpy，可以在一定程度上保护你的代码。&#10;更进一步的方式：把mpy文件藏到固件中去，这不光能保护代码，还能降低程序的内存占用，官方的描述如下：&#10;Cross-installing packages with freezing For the low-memory MicroPython ports, the process described in the previous section does not provide the most efficient resource usage,because the packages are installed in the source form, so need to be compiled to the bytecome on each import. This compilation requires RAM, and the resulting bytecode is also stored in RAM, reducing its amount available for storing application data. Moreover, the process above requires presence of the filesystem on a device, and the most resource-constrained devices may not even have it.</description></item><item><title>MicroPython入坑记(四)利用MQTT实现0编程远程控制（上）（用安卓手机）</title><link>http://yafengabc.github.io/embedded/p8684755/index.html</link><pubDate>Sat, 31 Mar 2018 23:33:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p8684755/index.html</guid><description>其实你被标题党骗了，0编程那是不可能的(￣▽￣)"&#10;不过也算近似零编程了哈哈&#10;MQTT我就不详细介绍了，毕竟看到这个标题进来的一般也就有些基础知识了。简单说说：&#10;MQTT是IBM发布的一个物联网协议，怎么说呢，微信大家都知道，微信是大家互相联系用的，MQTT其实就类似一个物联网的微信，各个设备间可以通过MQTT来沟通信息。&#10;其实设备间联系的协议有很多，比如TCP/UDP，HTTP等。MQTT作为很晚才出现的后辈，当然是解决以前协议的痛点的。&#10;1.TCP/UDP（SOCKET通讯）这个是物联网甚至是互联网的基础。在应用层面，这算底层了，优点当然是灵活，其实灵活，往往就意味着难用（比如C++就很灵活）你如果想开发一个基于TCP通讯，你需要考虑通讯的方方面面，比如断线重连，比如心跳包，比如加密传输，这些都得自己来实现，可能会占用你大量的调优时间。&#10;2.HTTP，HTTP协议大家都知道是网页传输协议，其实网页传输是HTTP的一种应用而已，HTTP属于对TCP的高层封装，是一种短链接协议，可用于设备间连接。基于HTTP协议的连接方式又有个名字叫RESTful，这个编程就简单了，因为短链接么，就不用考虑啥断线重连之类的了，并且python有urllib，requsets等库，通讯就是一句代码的事，简单至极，但也有缺点:包头太长，每次通讯都要重新连接一次。如果几秒或者几分钟以上通讯一次还差不多，如果太过频繁，不但浪费资源，也太占带宽。&#10;MQTT完美解决了上边这些协议的痛点，并且由于是个标准，现在各种语言，乃至安卓、IOS上都有现成的客户端，特别适合我这种懒得开发JAVA APP的懒人。这里介绍一个好用的安卓APP：ioT MQTT Panel，装了这个APP，设置一下，就能通过MQTT控制设备了，下边是我随便做了个界面：&#10;怎么样？是不是有点意思，值得说的是: 这是完全不需要编写代码的装上这个APP，设置一下就可以了，嗯这是跟我的树莓派连接的：&#10;树莓派上我接了一个继电器，一个超声波测距仪，手机客户端则是实时显示树莓派CPU温度、超声波测距的距离，以及控制继电器吸合、断开。&#10;下一篇，我会实战介绍下用这个APP控制ESP8266（nodemcu）来实现无线控制LED灯的亮灭。</description></item><item><title>MicroPython入坑记(四)利用MQTT实现0编程远程控制（下）（用安卓手机）</title><link>http://yafengabc.github.io/embedded/p8685028/index.html</link><pubDate>Sun, 01 Apr 2018 01:02:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p8685028/index.html</guid><description>先介绍下这个APP（ioT MQTT Panel）怎么用：&#10;安装我就不怎么说了，安装完打开软件是这个样子的：&#10;提示没有连接，点那个红色按钮创建一个连接：&#10;前两项都是随便填的，值得注意的的是那个Broker Web/IP Address，这个最好自己架个服务器，而不是用我填的这个测试用服务器，当然这里做个例子，先用这个凑数了，填完后点那个CREATE：&#10;可以看到名为”测试连接“的项目建成了。然后点近去：&#10;这理解释下：Topic可以理解为一个通道，比如开关，按下后会往ledctl这个通道里发ledon这个信息，再按一下会往ledctl通道里发ledoff这个信息，最终界面如下：&#10;下面，我们在MicroPython里边编写一点程序，用来接收这个界面发出的消息：&#10;首先，下载micropython的MQTT库：&#10;https://github.com/micropython/micropython-lib&#10;下找到：umqtt.simple&#10;这个目录下有几个例子，以及一个umqtt的目录(里边的simple.py就是库了)</description></item><item><title>MicroPython与Python速度对比</title><link>http://yafengabc.github.io/embedded/p9034158/index.html</link><pubDate>Mon, 14 May 2018 08:33:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p9034158/index.html</guid><description>首先说明，micropython跟python是没有任何可比性的，python作为一种通用的语言，在扩展性上不是micropython能比的，比如大量的库，可以方便的用C语言加模块提升速度，有pypy这样的带JIT的解释器，micropython是适合于单片机的系统虽然可以用C写lib，但是需要重新编译整个固件，此外，micropython也缺乏加载本地代码的功能，比如加载C便宜的so库。所以不要试图用micropython代替python，这不是一个好主意，除非micropython支持的库满足你的使用了。&#10;这篇文章主要是简单的对比这两个不同的实现的性能有何差别。&#10;测试代码有两个，一个是一个大循环，一个是递归计算斐波那契数列，例子比较简单，代码如下：&#10;try: import utime as time except: import time def bigloop(): s=0 for i in range(1000000000): s+=i def fib(n): if n==0: return 0 if n==1: return 1 return fib(n-1)+fib(n-2) t=time.time() bigloop() print("bigloop time:",time.time()-t) t=time.time() print("The 40th fibric is:",fib(40)) print("fibn time:",time.time()-t) 结果如下：&#10;[yafeng@ArchV ~]$ python micromark.py bigloop time: 60.44254755973816 The 40th fibric is: 102334155 fibn time: 48.39746880531311 [yafeng@ArchV ~]$ micropython micromark.py bigloop time: 51.92846608161926 The 40th fibric is: 102334155 fibn time: 65.70703196525574 可以看到，效率基本是一样的，循环micropython稍快一点，递归cpython稍快一点，顺便贴一下pypy pypy3的结果： [yafeng@ArchV ~]$ pypy micromark.py ('bigloop time:', 1.7053859233856201) ('The 40th fibric is:', 102334155) ('fibn time:', 7.795623064041138) [yafeng@ArchV ~]$ pypy3 micromark.py bigloop time: 1.2033970355987549 The 40th fibric is: 102334155 fibn time: 7.820451974868774 可以看到，pypy速都还是很明显的，喜闻乐见的是，pypy3甚至超过了pypy。</description></item><item><title>RP2040踩坑记 USB CDC串口无法下载程序(驱动)+不发送数据（DTR）</title><link>http://yafengabc.github.io/embedded/p18573354/index.html</link><pubDate>Thu, 28 Nov 2024 00:05:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p18573354/index.html</guid><description>这几天买个两块INA226自己搞了个简易功率计（C表），如下图：&#10;当时画板子就考虑兼容的板子多一点，支持Mini跟Super Mini类型的板子，一块搞了一个ESP32 C3（左），当时看RP2040-Zero便宜，就手贱又买了片RP2040-zero（右）。&#10;一开始RP2040是刷Micropython用的，测试很成功，功率计能正常读到电压电流。后来嫌micropython慢，所以打算换到Arduino平台重写一下，这一换不打紧，踩了两个大坑。&#10;我用的IDE是VScode+PlatformIO插件，支持巨量的板子，并且C++补全（Arduino）做的非常好，强烈吐血推荐：&#10;看里边也有RP2040的支持，新建一个，从串口调起，下边代码：&#10;#include &lt;Arduino.h&gt; void setup() { // put your setup code here, to run once: Serial.begin(115200); } void loop() { // put your main code here, to run repeatedly: Serial.println("Hello im from RP2040"); sleep_ms(1000); } 这算是Arduino的Hello world了，没理由跑不起来……&#10;然后……&#10;编译成功，下载失败……&#10;看了看Arduino生成的文件，有uf2，那就好说了，按住BOOT-&gt;按住RESET-&gt;松开RESET-&gt;松开BOOT，U盘出来了。把uf2丢进去，板子重启了，这时候出现一个串口，用串口助手连上去，咦？啥都没有？&#10;这就抽象了……难道……USB串口不是默认的Serial，是Serial1或者Serial2？然后去翻官方文档：&#10;Arduino-Pico 核心使用 USB ACM-CDC 模型实现基于软件的串行 USB 端口，以支持各种各样的操作系统。 Serial是 USB 串行端口，虽然Serial.begin()允许指定波特率，但由于它是基于 USB 的，因此会忽略该速率。（还请注意，此 USBSerial端口负责在上传过程中重置 RP2040，遵循 Arduino 标准 1200bps = 重置为引导加载程序）。 RP2040 提供两个基于硬件的 UART，具有可配置引脚选择。 Serial1是UART0，且Serial2是UART1。 这这这这……没错啊，就是默认的Serial……</description></item><item><title>Windows下串口高速接收数据的一些问题。</title><link>http://yafengabc.github.io/embedded/p18580397/index.html</link><pubDate>Sun, 01 Dec 2024 21:36:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p18580397/index.html</guid><description>书接上回，我不是搞了两块INA226的小板子么，下位机调通了，当然下一步就是上位机搞起，祭出C#+winforms三下五除二……如下图：&#10;好吧，丑是丑了点，winforms默认就是这B样。不就是玩玩么，玩的就是多快好省……巴拉巴拉……&#10;回正题，我们先算了算，INA226最快的转换时间是140us，需要读2个数据，就是电压、电流。也就是最快140+140=280us，加上处理，发送的时间，算他300us吧，也就是1ms的时间能够采集3+次，那一秒就得收3000+个数据。&#10;简单算一下：32*3000=96000也就是96K，常用串口的115200波特率勉强够用。另外还有USB通讯，现在的单片机一般支持USB2.0全速通讯，也就是12Mbps/s。串口的话一般CH340也能达到1M左右，基本是毫无压力。基于简单通用考虑&#10;当然是选串口了，另外这些单片机带的VCP是很快的，起码有3M/s的速度。&#10;问题出在windows上。众所周知，windows/linux这些OS都不是实时的。一个时间片通常是1ms，但是这不是固定的，有时也会10几ms不等，所以windows下接收数据需要缓冲区也就是高速设备发来的数据，我windows底层先收着，存在一个区域。&#10;等你程序来了一次性拿走就行了，所以即使windows能保证精确的1ms让你读一次串口，1毫秒也能读到3组数据。何况他根本保证不了，所以每次从串口读到的都是一堆数据并且因为发送的不是一个字节啊，还会出现读一半的情况。比如发送的一帧长度是20字节，你读上来30字节，前20字节是一完整的数据帧，后边10个字节是半个数据帧，还要跟后续读取的10个字节组成完整的数据帧。所以得想办法解决粘包的问题。还有一个就是时间问题，假设一次读到10个数据帧，这10个数据帧是同一个时间读到的，就不知道每个数据产生的具体时间了。&#10;先说说时间问题，前一阵子研究怎么给FNB58写一个客户端当时考虑过，FNB58是USBHID接口，这个接口最大的好处就是免驱，插上电脑就能用，并且如果早知道PID/VID的话可以写死在程序里，软件打开可以直接连接，不用选端口（串口就不行了，你插上他不一定是串口几了，需要自己选一下）USBHID的话是1ms一个64字节的数据帧，也就是500Kbps的样子，比串口慢，但是够用。他是1帧发送四组数据。平均10ms一个点，也就是大约40ms上报一个数据帧（其实我就是嫌它慢才会自己搞INA226）。因为当时参考了github上一个项目https://github.com/baryluk/fnirsi-usb-power-data-logger里边是这么处理的：&#10;每次读到数据后，比如100ms读一次，读到了10个点，那么我把10个点平均分配到这100ms之内就可以了，其实这样的话慢速还行，因为windows的实时性能保证40ms读一个数据帧，基本时间误差比较小。但是速度上去后，时间偏移就会比较大，另外一个稍微理想的方案就是上传的数据自带时间戳，这样就比较好了，我无论一次读到多少数据，反正自带时间，稍微处理下就能当时间用。然后问题又来了：你单片机时钟准吗？你如何保证现在单片机的时间是UTC（或者localtime）时间？时间戳精度够用吗？MD越来越复杂了……&#10;其实windows（或者安卓等其他设备）的时钟也是靠一个晶振产生的，其实也是不准的，但是架不住他们用网络对时，只要有网，他会每隔一段时间从网上获取时间对一下时间，所以windows（或者安卓之类）的时间是比较准的。后来嘛，我发现我想的有点多……你晶振再不准，也不会一天差一个小时吧……也就是差几分钟的精度，我又不记录那么长时间的记录，真要计那么多数据，那一般也不会需要那么高的采样率了（因为我试过给数据记log，开几十秒能记几兆的数据，时间长的话还不得把硬盘塞满……）。再就是时间戳精度问题，因为数据采集时间时低于1ms的，所以需要精确到us，那么32位的时间戳多少时间溢出呢1.19小时……要是记录手机充电的话，还真不怎么够，好在最快140us才一个点，精确的到10us的话就能11.9小时，100us的话就是119小时。足够了（当然可以用64位的时间戳，那样时间就是天文数字了，用32位是为了节省点通讯带宽）。经过一番思考，决定把协议弄成这个样子：&#10;AA FF+XXXX XXXX（时间戳UINT）+XXXX XXXX（电压FLOAT）+ XXXX XXXX（电流FLOAT）+55，也就是15字节，其实还可以简化，比如包头只有AA，包尾也可以去掉，电压电流可以传16位的整型那样9字节就够了。之所以搞成这样，原因比较复杂，因为我一会儿想在单片机上标定电流，一会儿想在上位机上，所以频繁的改协议，后来想想干脆浮点数算了，先定下来再说。&#10;至于字头为啥用了两个，这个说出来丢人，因为一开始吧为了解决粘包问题，我用了两个线程+一个队列，一个线程收，收到后扔到队列，一个线程解析协议从队列取值（网上这么教我的），后来发现，频繁的解包出错甚至在包头AA FF正确的时候，收到的电压电流乱跳，后来又加了个包尾，才能保证每次读到的数据都正确（但是通过记录失败的次数，每秒都有大量的错误次数）直到我意识到我用的队列是线程不安全的……开始是换了C#自带的ConcurrentQueue，错误立刻就降到了0，后来嫌CPU占用率高，想到虽然队列线程安全，但是肯定会在锁上竞争，把接收跟解包放到了一个线程，再到后来想到系统的串口缓冲区其实也是一个队列，所以就干脆把队列取消了……代码如下的样子：&#10;byte Dequeue; if (serial.IsOpen &amp;&amp; serial.BytesToRead&gt;0) {while(serial.BytesToRead&gt;=15) { Dequeue=(byte)serial.ReadByte();; if (Dequeue==0xAA) { Dequeue=(byte)serial.ReadByte(); if (Dequeue==0xFF) { for(int i=0;i&lt;=11;i++) { result[i]=(byte)serial.ReadByte(); } Dequeue=(byte)serial.ReadByte(); if (Dequeue==0x55) { timestamp = BitConverter.ToUInt32(result,0); voltage = BitConverter.ToSingle(result,4)*1.25/1000; current = BitConverter.ToSingle(result,8)*2.24/100; power = voltage*current; rev_frame_counter++; } else { err_frame_counter++; } } } } } 首先判断串口缓冲区是不是大于等于15个字节（因为协议是15字节），如果有足够的字节数，就先读一个字节，判断是不是帧头（0xAA），为啥用了俩字头呢，因为数据里也可能有0xAA这个数据啊，如果检测到0xAA就直接读剩下的14字节，最后发现不对就至少丢掉两个帧的数据，数据里AA FF这样的可能性就很小了，当然也可以AA BB，都无所谓，验证帧头正确然后读12个字节的数据，一个整数(时间戳)，两个浮点数（电压，电流），至于功率就是电压x电流，最后验证字尾是不是正确。&#10;如果不对就丢掉，对的话就解码。&#10;可以看到没有用队列（利用了串口缓冲区自己就是队列的特点）。&#10;这样可以做到0出错，0丢包（当然排除串口传输出错的情况）。当然帧尾可以用校验和，或者CRC之类的代替固定值，那样能更大限度的保证数据的正确性。我没这么干主要是嫌CPU占用率高。&#10;回到最开始的图，每秒只能接收1200帧数据，也就是每ms一个多一点的数据，是因为这个单片机我用的RP2040（以前从来没用过，只是看到这个板子能焊到我做的PCB上，就直接买了），硬件I2C针脚跟我的板子不兼容，所以只能用软件I2C协议&#10;读的比较慢，得300多us才能读一个值，1200帧/秒已经是极限了。这时候我电脑CPU占用率10%左右。</description></item><item><title>串口助手极限测试</title><link>http://yafengabc.github.io/embedded/p18581812/index.html</link><pubDate>Mon, 02 Dec 2024 14:13:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p18581812/index.html</guid><description>昨天测试串口助手，发现高速数据流对串口数据压力很大，经测试，压力主要来自windows的组件的接收字串并渲染的速度。&#10;测试代码如下：&#10;byte result; while(true) { while(serial.IsOpen &amp;&amp; serial.BytesToRead&gt;=0) { byte_to_read=serial.BytesToRead; result = (byte)serial.ReadByte(); rev_bytes_counter++; rev_frame_counter=rev_bytes_counter/15; } System.Threading.Thread.Sleep(1); } } 现在是15个字节一个帧，结果如下：&#10;现在接收速率是47KByte/s，3167帧/s，传输速率是427743bps，做到了实时接收，如果加一句显示的代码：&#10;while(serial.IsOpen &amp;&amp; serial.BytesToRead&gt;=0) { byte_to_read=serial.BytesToRead; result = (byte)serial.ReadByte(); rev_bytes_counter++; textBox1.AppendText(result.ToString("X2")+" "); rev_frame_counter=rev_bytes_counter/15; } 速度立刻就炸了，每秒钟只能显示32个帧，然后大量的数据呆在串口缓冲区出不来，最后就boom……&#10;后来我想，应该就是windows的textbox刷新太慢了，我一次写入大量数据，不就能缓解这个问题了？&#10;改成如下代码：&#10;byte[] toview = new byte[256]; while(true) { while(serial.IsOpen &amp;&amp; serial.BytesToRead&gt;=256) { byte_to_read=serial.BytesToRead; serial.Read(toview,0,256); textBox1.AppendText(BitConverter.ToString(toview).Replace("-"," ")); rev_bytes_counter+=256; rev_frame_counter=rev_bytes_counter/15; } 也就是一次往textbox里边扔256个字节，测试如下：</description></item><item><title>INA226折腾笔记（一）</title><link>http://yafengabc.github.io/embedded/p18573281/index.html</link><pubDate>Sun, 22 Dec 2024 23:02:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p18573281/index.html</guid><description>其实也算不上折腾，这个模块很简单就一个简单的I2C模块。随便搞个单片机就能读写，之所以玩这个，主要还是对手上的USB表不满意（换句话说就是达不到我的需求）&#10;我手上有几个USB表&#10;左边那个高仿版FNB58，右边那个是科维斯的CC表型号为KWS-1902C，平时就是测测手机充电功率之类的，也够用，FNB58还带了个客户端，如下图：&#10;平时用绝对够用了，并且精度还可以（特别小的负载下精度不是很理想）。&#10;最所以自己折腾INA226，主要是我最近玩耳机测小尾巴的功耗（20mw-1W）那两个U表精度不够，寨板FNB58精度还算可以，但是因为不是直通表，插上小尾巴（USB声卡）会有严重的噪音。另外一个CC表低功耗下（&lt;50mw）直接显示0，网上虽然有一堆堆的U表卖，但具体什么情况还未可知，所以嘛，自己动手搞起。&#10;淘宝上INA226的模块巨便宜，几块钱&#10;一共16块钱，为了标定板子，我还买了块电阻板，如下：&#10;然后画了块电路板，去淘宝5块钱打印了5块，最后一顿焊，成品如下：&#10;INA226买的时候，注意那个采样电阻，上边两个板子一个是R100（0.1欧），一个是R010（0.01欧），理论上说，这个电阻越大，精度越高，代价就是量程变小。INA226通过测量电阻两边的电压来测量电流。最大电压是81.92mv，所以可以简单地通过电压/电阻来确定最大量程，0.08192/0.1=0.8192A 0.08192/0.01=8.192A。也就是这两个板子一个量程是0.8A，一个是8A。&#10;但是我上边的板子设计失误了，为啥呢？可以看到我的红圈处的那两个接线柱是没有供电的。&#10;我一开始的想法是：反正我也不测量大电流，所以没必要从这两个点取电，所以就从右上角的IN+ IN-代替了这两个大焊盘。但是我忽略了电路也是有电阻的！因为电路板的铜层很薄，电阻相对0.01欧来说不可忽略。&#10;结果就是导致：采样电阻阻值改变了。0.1欧版本还好，0.01欧版本直接量程减半（标定为0.0226欧左右）所以我现在这两个板子，一个量程是0.73A，一个是3.6A左右（虽然变相提高了精度）。&#10;关于INA226的寄存器：&#10;寄存器0 配置寄存器，默认1次采样，采样周期1.1ms，连续采样（0x4127）。通常情况下，这个配置已经可以用了&#10;寄存器1 采样电压，也就是采样电阻两端的电压，电阻足够精确的话，这个值*0.08192/采样电阻/32768就是电流值了。&#10;寄存器2 VBUS电压，也就是电源电压，比如5V，10V，读出值*0.0025就是电压。&#10;寄存器3 功率，如果标定寄存器不设置，这个值默认是0</description></item><item><title>INA226折腾笔记（二）</title><link>http://yafengabc.github.io/embedded/p18622825/index.html</link><pubDate>Mon, 23 Dec 2024 00:09:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p18622825/index.html</guid><description>接上文，接个500欧的电阻作为负载：&#10;&gt;&gt;&gt; i2c.readfrom_mem(64,1,2).hex() '005a' &gt;&gt;&gt; int(0x5a) 90 按照模块上采样电阻0.01欧来算：&#10;90*81.92mV/32768=0.225mV&#10;0.225mV/0.01Ω=22.5mA&#10;但是，5V，500欧，应该是10mA，所以……&#10;电阻大了一倍有余，放大系数为2.25……&#10;电阻换成100欧&#10;&gt;&gt;&gt; i2c.readfrom_mem(64,1,2).hex() '01ba' &gt;&gt;&gt; int(0x1ba) 442 442*81.92mV/32768 = 1.105mV&#10;1.105/0.01 = 110.05mA&#10;实际应该是50ma，放大系数为2.21&#10;继续改变电阻，为200欧&#10;&gt;&gt;&gt; i2c.readfrom_mem(64,1,2).hex() '00dd' &gt;&gt;&gt; int(0xdd) 221 系数2.21&#10;改编为50欧：&#10;&gt;&gt;&gt; i2c.readfrom_mem(64,1,2).hex() '0367' &gt;&gt;&gt; int(0x367) 871 系数为2.177&#10;因为负载也有精度问题。经多次测量，系数大约2.2左右。&#10;所以程序如下：&#10;import micropython i2c = I2C(0, scl=Pin(8), sda=Pin(9), freq=1000000) i2c.writeto_mem(64,0,b"\x41\x27") @micropython.native def read_ina226(): while True: ret1 = i2c.readfrom_mem(64,1,2) ret2 = i2c.readfrom_mem(64,2,2) current = struct.unpack("&gt;h",ret1)[0]*0.113 voltage = struct.unpack("&gt;h",ret2)[0]*1.25/1000 print("voltage={0}V,current={1}mA,power={2}mW,R={3}".format(voltage,current,voltage*current,1000*voltage/current)) time.sleep(1) read_ina226() MPY: soft reboot&#10;voltage=5.07125V,current=25.086mA,power=127.2174mW,R=202.1546&#10;voltage=5.07125V,current=24.973mA,power=126.6443mW,R=203.0693&#10;voltage=5.07125V,current=24.973mA,power=126.6443mW,R=203.0693&#10;voltage=5.0725V,current=25.086mA,power=127.2487mW,R=202.2044&#10;voltage=5.07125V,current=24.973mA,power=126.6443mW,R=203.0693&#10;voltage=5.07125V,current=24.973mA,power=126.6443mW,R=203.0693&#10;voltage=5.07125V,current=24.973mA,power=126.6443mW,R=203.0693&#10;voltage=5.07125V,current=24.86mA,power=126.0713mW,R=203.9924&#10;voltage=5.07125V,current=24.86mA,power=126.0713mW,R=203.9924</description></item><item><title>Arduino UNO ESP8266 ESP32 MicroBit IO操作速度对比记录</title><link>http://yafengabc.github.io/embedded/p11197001/index.html</link><pubDate>Tue, 16 Jul 2019 19:32:00 +0800</pubDate><guid>http://yafengabc.github.io/embedded/p11197001/index.html</guid><description>ESP8266 Arduino C编程&#10;void setup(){ pinMode(13, OUTPUT); } void loop(){ digitalWrite(13,HIGH); digitalWrite(13,LOW); digitalWrite(13,HIGH); digitalWrite(13,LOW); } 可以看到，相邻的两次digitalWrite之间是958ns(纳秒)，而两次loop之间是6.6us(微秒)，速度还是很快的，而Arduino UNO得到了如下结果：&#10;可以看到无论是loop内的，还是loop之间的间隔，都是接近的，都在3.25-3.7之间，速度比ESP8266慢几倍的水平（考虑到时序都是在loop内完成）&#10;下边是同样的代码ESP32得到的结果&#10;相比ESP8266，还是有很大进步的（综合估计得有10倍）&#10;下边是Micropython的：&#10;import machine pin13 = machine.Pin(13, machine.Pin.OUT) while True: pin13.value(1) pin13.value(0) pin13.value(1) pin13.value(0) 先上ESP32的：&#10;嗯，Arduino水平。&#10;再看ESP8266：</description></item></channel></rss>