分享IT技术,分享生活感悟,热爱摄影,热爱航天。
Raspberry Pico作为一种超小型化的开发板,相比普通版的树莓开发板,其更像是一个单片机——其没有操作系统,启动后自动加载默认的代码。但带来的好处也是更加简单可控,对于处理简单的硬件控制更加好用,同时体积和功耗也更低,一些精简化的Pico体积仅有邮票大小,非常适合部署在体积极为有限的设备内,并且价格仅有几十元,相比标准的树莓也有巨大的优势。
Pico同样可使用C或Python进行开发,但既然选择使用Pico那肯定用Python会更加简单高效,但其不能使用完整的Python系统,使用的为MicroPython,其实现了Python的基本功能,同时还提供了一些针对Pico的功能,例如UART、I2C端口的通信API等。代码直接可运行,不需要进行编译,调试也非常方便,不过为了保护代码也可以将代码编译成二进制。
首次使用时需要对Pico进行固件的安装,按住Pico的Boot按键将Pico通过USB连接至PC,此时会识别出一个移动磁盘设备,此时将下载的固件拖入其中即可完成固件的安装,完成后Pico会重启并开始加载固件。
开发可以使用Thonny,其为一个针对MicroPython和Pico的IDE,可以直接连接Pico,并上载代码运行和查看运行结果。
Pico在加载固件后会自动运行main.py,为此可将代码的入口放在这里,通电启动后即开始执行这里的代码,执行结束后退出,一些版本的Pico中也支持通过一个按键进行复位从头再开始执行一次main.py。
对于一些需要一直保持运行状态的应用场景,可以将其主循环放在main.py中,其他的代码放在其余文件中,通过import引入进行调用。
通信接口
Pico有26个GPIO针脚,其中很多针脚可以实现多种用途,并且都可以进行PWM信号输出,详细可参考官方的文档
#引入Pico中的基础API from machine import Pin, PWM, UART, I2C; #定义对应的通信针脚代号 I2C_SCL_PIN = 3; I2C_SDA_PIN = 2; UART_TX_PIN = 0; UART_RX_PIN = 1; #初始化对应通信接口 i2c = I2C(1, scl=Pin(I2C_SCL_PIN), sda=Pin(I2C_SDA_PIN)); uart = UART(0, baudrate=9600, tx=Pin(UART_TX_PIN), rx=Pin(UART_RX_PIN));协程实现异步操作
由于MicroPython中没有完成的多线程支持,为此只能通过协程实现IO部分的异步处理
#接收串口数据 async def uart_rx_task(uart, q): #循环接收数据 while True: #如果收到数据则放入队列中 if uart.any(): data = uart.read(); q.append(data); else: pass; #交出执行权给主线程 await asyncio.sleep(0.1); #主程序 async def main(): #启动数据接收协程 asyncio.create_task(uart_rx_task(uart, q)); #主循环 while True: #处理队列中的消息 while len(q) > 0: data = q.popleft(); #处理其他工作... #交出执行权给接收协程 await asyncio.sleep(0.05);在main.py中启动主程序的写入如下
if __name__ == '__main__': asyncio.run(main());
有时我们不想暴露Pico中代码的实现细节,可以使用工具将代码编译成二进制,虽然编译成的二进制也可通过反编译等手段进行代码的还原,但还原后的代码的可读性要差很多,还是可以起到一定的保护作用。一般推荐的工具为mpy-cross,可以将python代码编程成为mpy文件。编译时需要指定CPU架构,Pico对应的CPU架构为armv6m。
mpy-cross -march=armv6m core.py会得到一个core.mpy文件,这里不使用main.py的原因是因为Pico不会自己自动运行main.mpy,所以为了能够自动运行可以将代码封装到core.py中编译为core.mpy,另外写一个main.py调用core.mpy中的运行方法执行代码。
import core.main; if __name__ == '__main__': core.main();
控制器局域网总线(CAN,Controller Area Network)是一种用于实时应用的串行通讯协议总线,它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一。CAN协议用于汽车中各种不同元件之间的通信,以此取代昂贵而笨重的配电线束。
CAN通讯是异步通讯,只有CAN_H和CAN_L两条信号线,共同构成一组差分信号线,以差分信号的形式进行通讯。使用闭环总线网络,速度快,距离短,它的总线最大长度为40m,通讯速度最高1Mbps,总线两端各有一个120欧的电阻。
CAN总线上可以挂载多个通讯节点,节点之间的信号进过总线传输,实现节点间通讯。由于 CAN 通讯协议不对节点进行地址编码,而是对数据内容进行编码的,所以网络中的节点个数理论上不受限制,只要总线的负载足够即可,可以通过中继器增强负载。
CAN通信协议分为标准格式和扩展格式,在使用层上两者主要的区别在于ID的长度不同,标准帧为11位,扩展帧为29位,传输数据的部分都是0~8个字节的数据段(数据前用4位的标识数据的长度)。
CAN接口在Linux系统中和以太网接口一样作为一个通信接口,可以通过ifconfig和ip命令进行配置和启动。以树莓派中常见的MCP2515为例,除了在内核中配置引导对应的模块外。可通过下列命令对CAN接口进行启用
ip link set can0 up type can bitrate 1000000 ifconfig can0 txqueuelen 65536 ifconfig can0 up如果需要在开机时启动CAN接口,可以将相关配置放在rc.local或udev中。
KERNEL=="can*" SUBSYSTEM=="net" ACTION="add" PROGRAM="/sbin/ifconfig $name txqueuelen 1024" RUN+="/usr/bin/ip link set up $name type can bitrate 500000"并执行
udevad control --reload-rules && udevadm trigger即可实现CAN接口的自动启动。
可使用命令行工具cansend和candump进行进行CAN通信的快速测试,其格式为
#接收并输出can0接口的数据 candump can0 #接受并输出任何can接口的数据 candump any #从can0端口发送标识为123,0x11223344的数据 cansend can0 123#11223344并且在调试时可以将数据同时发送给自己一份,以便进行调试,可在启动can的命令结尾加上loopback=on
在python中使用CAN通信,可安装python的can扩展,可使用pip或系统软件包管理系统进行安装python-can,其使用的方式如所示
#使用can0接口,波特率500kbps,loopback模式为关闭 can0 = can.interface.Bus(channel = 'can0', \ interface='socketcan', bitrate=500000, local_loopback=False); #创建id为0x00000625的CAN帧,数据为data,格式为标准帧 msg = can.Message(arbitration_id=0x00000625, \ data=bytearray([0x40, 0x05, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00]), is_extended_id=False); #发送数据 can0.send(msg); #接收数据,等待时间1秒 ret = can0.recv(1.0);