【2025电源模块创意大赛】超轻型自制飞机用航电系统——焊接调试过程踩坑分享

一、背景

国内个人自制的超轻型载人飞机已经有一定市场,但是因为成本及空间限制,大多数人都没有安装飞机航电设备,日常飞行缺乏设备提供可靠的数据参考。
现有民航或专用航电仪表因为法规因素,产品认证成本高昂,导致销售价格动辄上万,功能复杂点甚至十几万,远远超过大家的承受能力,急需一款简单易用、成本相对低廉的航电装置。

给个参考吧,下面这个MGL出的高度、空速、升降速率表要卖500美刀,这还是不含税不含运费的价格,对于国内大多数飞机爱好者来说,这个价格简直是天价了,就不用说其他更高级的航电仪表,随随便便几万十几万。图片里的仪表拆开后里面简单的令人发指,做工非常山寨,我自己手工做出来的质感都更好。内部结构简单到不行,只有电源、MCU、RTC、EEPROM、LCD屏和RS232接口、两个气压传感器。

基于上述原因,计划开发一款基于CANBUS总线的可扩展航电系统,根据需要可使用基础模块并通过多个拓展功能模块实现更加丰富的功能。目前已经在开发部分模块,其余的陆续开发中,此处介绍的是显示模块。

二、系统框图

该模块主要作用是通过CANBUS获取姿态、发动机监控、空气电脑等子模块的数据,并通过LCD屏幕进行展示,以便飞行员及时了解飞机姿态、高度、空速、升降率、航向、飞行时间以及发动机转速、缸头温度、机油压力等参数信息。

板载RTC电路计算实时时间,以便计算飞行时间等参数。

显示屏采用双联屏(主要是手头刚好有),单块分辨率270*160,并且通过两个FPC排线接到板子。因为飞机在空中可能面临阳光直射情况,毕竟不是波音737那种大客机,一般自制飞机都是裸奔状态,也就是连座舱罩都没有,屏幕会面临阳光直射情况,需要足够背景光并且支持亮度调节,这里通过光敏电阻获取环境光照强度,并且通过pwm恒流背光电路调节。

模块设计了RS232接口以便跟其他商用航电通讯,获取数据,比如通过Garmin G5获取姿态高度等辅助信息。为了更好的扩展性,还保留了IIC接口外扩,为了适配外部传感器的电源电压,保留了3种跳线可选。输入信号通过编码器开关,可以打开菜单设置参数以及校准,并且作为气压修正数据输入方式,数据存储到EEPROM里保证掉电不丢失。

主控采用了RP2040,为了方便调试这里外加了Type-C接口,未来可能会内置CAN BOOTLOADER,以便通过CAN总线进行集中软件版本管理和升级。

三、PCB设计

板子采用四层电路设计,顶层用来布置元器件,背面用来贴合LCD屏幕。

这里只展示很小一部分元器件,实际花了上百,感觉为了炒一盘菜而买了一套家具。

四、踩坑记录

  1. mcu焊接后连接电脑没反应

因为没开钢网(现在后悔死了,真不应该省这点),所及焊接是通过手工涂锡膏并通过加热板铁板烧来搞的。首先焊接的MPS电源模块MPM38222,上电后测试非常顺利,电压输出稳稳的。

然后逐步焊接了外围芯片和mcu,焊接过程是逐步进行的,也就是焊接一部分会测测是否短路。最后焊接完mcu后上电没有短路情况,供电也ok,但是通过USB线接电脑后没任何反应(RP2040在flash没固件时候接电脑会自动生成U盘,没任何反应就是不正常)。

测量MCU的供电和核心1.1V供电也都正常,顿时把我给整不会了。最开始怀疑usb部分焊接问题,或者typec插座温度太高焊接坏了,补焊和重新更换后也不行。

也怀疑是不是mcu或者flash坏了,并且替换到其他最小系统板进行验证,结果也不工作。(开始没想到是mcu焊接问题,虽然之前有过焊接导致mcu不工作的情况,这QFN真不好焊接。但是通过体视显微镜看了下,没太大问题。)

无奈又紧急通过淘宝买了几片rp2040,也是不凑巧,手头的刚好用完了,于是又浪费一大笔运费。结果焊接上去后也不工作,此时峰回路转让我又怀疑是不是mcu焊接问题,索性再次补焊了mcu,这次终于识别了。

1 个赞

2.USB输入电源设计错误

通过type-c接口供电,发现板子没电,测试后发现只有+5V有4.8V,重新检查原理图发现一个低级设计错误。

通过原理图可以看出来,usb过来的5V通过二极管接到了MPM38222的第二路输出+5V上,但是系统VCC实际是MPM38222的第一路3.3V输出,以后还得重新修改一版。

3.IIC接口调试

这里IIC接口调试没啥大问题,只是给大家分享下。

【原理图】

【测试源代码】

此处为了简化测试过程,用的micropython环境,主要是简便。

# I2C Scanner MicroPython
from machine import Pin, I2C
import time

led = Pin(22, Pin.OUT)
i2c = I2C(1, scl=Pin(27), sda=Pin(26),freq=100_00)
print('I2C SCANNER')

def main():
    while True:
        devices = i2c.scan()
        if len(devices) == 0:
          print("No i2c device !")
        else:
          print('i2c devices found:', len(devices))
          for device in devices:
            print("I2C hexadecimal address: ", hex(device))
            led.toggle()
            time.sleep(3)
if __name__ == "__main__":
    main()

【测试结果】

4.LED调试

【原理图】

这里设计用了一个共阳极双色LED指示灯,为了方便测试我焊接了个0603普通LED,主要是忘记买了 :joy:

【测试源代码】

# I2C Scanner MicroPython
from machine import Pin, I2C
import time

pin22 = Pin(22, Pin.OUT)
i2c = I2C(1, scl=Pin(27), sda=Pin(26),freq=100_00)

#LED Test
while True:
  pin22.value(1)
  time.sleep(0.5)
  pin22.value(0)
  time.sleep(0.5)

【测试结果】

PixPin_2025-11-15_22-17-47

5.CAN接口芯片XL1042输出电压异常

测试CAN接口过程中发现,通过CAN总线发送报文时,接口芯片与控制器连接的TXCAN有波形,但是CANH和CANL上电平非常奇怪,电压很低,根本没办法和调试工具通讯。最开始怀疑接口芯片挂了,因为TP16测试点上TXCAN波形是正常的,说明CAN控制器能正常工作。

【原理图】

这里按常规排查思路,万用表测量了下CAN总线上电压和终端电阻值,测量发现终端电阻只有12.1Ω,正常是120Ω,因为要调试,所以实际板子上终端电阻是生效的。

进一步测量发现板子上的终端电阻就是120Ω,这很奇怪,因为电阻是我从样品册里拿出来的,不应该啊,于是重新搞了一颗出来测量,结果发现样品册里120Ω位置放的居然就是12Ω的。。。。

6.CAN波特率异常

CAN控制器采用的是XL2515,通过SPI接口控制,该芯片可以Pin2Pin替换MCP2515,这是应用很广泛的一颗CAN控制器。测试过程中发现输出波形频率只有400多Hz,最开始测试代码我是从一款开发板的示例copy过来的,按理说一样的芯片不应该出问题。

仔细对比两款设计后发现我用的是8MHz晶振,而开发板用的16MHz晶振。

【原理图】

【问题排查】

分析XL2515芯片的Datasheet,与波特率有关的是CNF1、CNF2和CNF3这几个寄存器。这几个寄存器的值计算起来挺繁琐的,于是乎网上找了个配置软件,根据自己需要的波特率可以生成所需的寄存器值,很方便。这我也贴上来,不过软件在win10下运行有点麻烦,需要参照下面链接操作,不然会报错。

波特率计算软件:下载链接

***********************************************************************

注册OCX或DLL文件时。这种错误通常是由于权限不足或系统缺少必要的组件导致的。

示例

regsvr32 C:\Windows\SysWOW64\mscomm32.ocx

执行上述命令时,可能会出现错误代码0x8002801c。

解决方法

1. 复制必要的组件

首先,将所需的OCX组件复制到系统目录中。例如,将msinet.ocx或MSCOMCTL.OCX复制到C:\Windows\SysWOW64目录下。

2. 以管理员身份运行命令提示符

在C:\Windows\SysWOW64目录下找到cmd.exe文件,右键单击并选择“以管理员身份运行”。

3. 注册组件

在管理员命令提示符中输入以下命令来注册组件:

regsvr32 %windir%\SysWOW64\msinet.ocx

************************************************************************************

最终修改了波特率相关的这个数据,就可以跟CAN调试器正常通讯收发数据了。

【测试源代码】

from machine import Pin, SPI, PWM, I2C
import time

XL2515_SPI_PORT = 0
XL2515_SCLK_PIN = 18
XL2515_MOSI_PIN = 19
XL2515_MISO_PIN = 16
XL2515_CS_PIN = 17
XL2515_INT_PIN = 20


class RP2350_CAN:
    def __init__(self, rate_kbps = "100KBPS", spi_cs = XL2515_CS_PIN, irq = XL2515_INT_PIN, spi_port = XL2515_SPI_PORT, spi_clk = XL2515_SCLK_PIN,spi_mosi = XL2515_MOSI_PIN, spi_miso = XL2515_MISO_PIN,  spi_freq=10_000_000):
        self.can_rate_arr = {
            "5KBPS"   : [0x27, 0XB4, 0x06],
            "10KBPS"  : [0x13, 0XB4, 0X06],
            "20KBPS"  : [0x09, 0XB4, 0x06],
            "50KBPS"  : [0x03, 0XB4, 0x06],
            "100KBPS" : [0x01, 0xB4, 0x06],
            "125KBPS" : [0x01, 0xB9, 0x04],
            "250KBPS" : [0x01, 0x90, 0x02],
            "500KBPS" : [0x00, 0x90, 0x02],
        }

        self.spi = SPI(spi_port, spi_freq, polarity = 0, phase = 0, bits = 8, sck = Pin(spi_clk), mosi = Pin(spi_mosi), miso = Pin(spi_miso))
        self.cs = Pin(spi_cs, Pin.OUT)
        self.cs(1)
        self.int = Pin(irq, Pin.IN, Pin.PULL_UP)
        self.int.irq(handler = self.int_callback, trigger = Pin.IRQ_FALLING)
        self.recv_flag = False
        self.reset()
        time.sleep(0.1)
        self.config(rate_kbps)
        
        
    def config(self, rate_kbps):
        CNF3 = 0x28
        CNF2 = 0x29
        CNF1 = 0x2A
        
        TXB0SIDH = 0x31
        TXB0SIDL = 0x32
        TXB0DLC = 0x35
        
        RXB0SIDH = 0x61
        RXB0SIDL = 0x62
        RXB0CTRL = 0x60
        RXB0DLC = 0x65
        
        RXF0SIDH = 0x00
        RXF0SIDL = 0x01
        RXM0SIDH = 0x20
        RXM0SIDL = 0x21
        
        CANINTF = 0x2C
        CANINTE = 0x2B
        
        CANCTRL = 0x0F
        REQOP_NORMAL = 0x00
        CLKOUT_ENABLED = 0x07
        CANSTAT = 0x0E
        OPMODE_NORMAL = 0x00
        
        self.write_byte(CNF1, self.can_rate_arr[rate_kbps][0])
        self.write_byte(CNF2, self.can_rate_arr[rate_kbps][1])
        self.write_byte(CNF3, self.can_rate_arr[rate_kbps][2])
        self.write_byte(TXB0SIDH, 0xFF);
        self.write_byte(TXB0SIDL, 0xE0);
        self.write_byte(TXB0DLC, 0x40 | 0x08);

        # Set RX
        self.write_byte(RXB0SIDH, 0x00);
        self.write_byte(RXB0SIDL, 0x60);
        self.write_byte(RXB0CTRL, 0x60);
        self.write_byte(RXB0DLC, 0x08);

        self.write_byte(RXF0SIDH, 0xFF);
        self.write_byte(RXF0SIDL, 0xE0);
        self.write_byte(RXM0SIDH, 0xFF);
        self.write_byte(RXM0SIDL, 0xE0);

        # can int
        self.write_byte(CANINTF, 0x00); # clean interrupt flag
        self.write_byte(CANINTE, 0x01); # Receive Buffer 0 Full Interrupt Enable Bit

        self.write_byte(CANCTRL, REQOP_NORMAL | CLKOUT_ENABLED)
        dummy = self.read_byte(CANSTAT)
        if ((dummy & 0xe0) != OPMODE_NORMAL):
            print("OPMODE_NORMAL")
            self.write_byte(CANCTRL, REQOP_NORMAL | CLKOUT_ENABLED) #set normal mode
        
    def send(self, can_id, data):
        TXB0CTRL = 0x30
        TXB0SIDH = 0x31
        TXB0SIDL = 0x32
        TXB0EID8 = 0x33
        TXB0EID0 = 0x34
        TXB0DLC  = 0x35
        TXB0D0   = 0x36
        dly = 0
        while ((self.read_byte(TXB0CTRL) & 0x08) and (dly < 50)):
            dly += 1
            time.sleep_ms(1)
        
        self.write_byte(TXB0SIDH, (can_id >> 3) & 0XFF)
        self.write_byte(TXB0SIDL, (can_id & 0x07) << 5)
        
        self.write_byte(TXB0EID8, 0)
        self.write_byte(TXB0EID0, 0)
        self.write_byte(TXB0DLC, len(data))
        for i in range(len(data)):
            self.write_byte(TXB0D0 + i, data[i])
        self.write_byte(TXB0CTRL, 0x08)
    
    def recv(self, can_id):
        RXB0SIDH = 0x61
        RXB0SIDL = 0x62
        CANINTF = 0x2C
        CANINTE = 0x2B
        RXB0DLC = 0x65
        RXB0D0 = 0x66
        
        if self.recv_flag == False:
            return None
        self.recv_flag = False
        
        self.write_byte(RXB0SIDH, (can_id >> 3) & 0XFF)
        self.write_byte(RXB0SIDL, (can_id & 0x07) << 5)
        
        while True:
            if (self.read_byte(CANINTF) & 0x01):
                len = self.read_byte(RXB0DLC)
                buf = bytearray(len)
                for i in range(len):
                   buf[i] = self.read_byte(RXB0D0 + i)
                self.write_byte(CANINTF, 0);
                self.write_byte(CANINTE, 0x01)  # enable
                self.write_byte(RXB0SIDH, 0x00) # clean
                self.write_byte(RXB0SIDL, 0x60)
                return buf
    def int_callback(self, pin):
        self.recv_flag = True
    
    def reset(self):
        CAN_RESET = 0xC0
        self.cs(0)
        self.spi.write(bytearray([CAN_RESET]))
        self.cs(1)
    def read_byte(self, reg):
        CAN_READ = 0x03
        self.cs(0)
        self.spi.write(bytearray([CAN_READ, reg]))
        data = self.spi.read(1)
        self.cs(1)
        return data[0]
    def write_byte(self, reg, data):
        CAN_WRITE = 0x02
        self.cs(0)
        self.spi.write(bytearray([CAN_WRITE, reg, data]))
        self.cs(1)
        
        
if __name__ == "__main__":
    can = RP2350_CAN()
    while True:
        can.send(0x123, [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88])
        recv_data = can.recv(0x123)
        if recv_data != None:
            print("recv:", [hex(i) for i in recv_data])
        time.sleep(1)

【测试结果】

发送:软件里发送了ID为0x123,数据为0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88的报文,在CAN调试工具里可以正常接收。

接收:在CAN调试工具里手动发送数据,在micropython的REL窗口里可以看到对应数据

1 个赞