# 使用指南
***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.***
---
使用指南旨在为用户提供 **Quectel Pi M1 / L1** 智能主控板的全方位使用指导,涵盖设备规格介绍、电源供给、显示及多类外设(摄像头、Wi-Fi/BT、蜂窝网络、以太网、音频、USB等)的功能说明、操作方法与注意事项等内容,帮助用户快速认知设备性能、掌握使用流程、解决应用难题,为设备的调试、部署及运维提供清晰、详实的参考依据。
# 规格介绍
请移步至 [规格介绍](<../../../使用指南/规格介绍/规格介绍.md>) 页面查看。
# 显示
## MIPI
本文档详细介绍了 **Quectel Pi M1 / L1** 智能主控板的 **MIPI—DSI** 显示屏使用方法,并以微雪 **8-inch DSI** 接口 **IPS** 电容触摸屏为例进行详细说明。
### MIPI屏幕连接
#### 硬件接口连接
> 此屏幕共有4处接口可供电,任选一处即可,示例选择“1”处供电。
1. **Type-C** 接口。
2. **I2C** 通讯与供电接口。
3. **5V** 输入输出接口。
4. **I2C** 通讯与供电接口,需将螺丝孔对准 **Quectel Pi M1 / L1** 智能主控板拧紧,让顶针与 **40-pin** 连接牢固。
```{image} images/image_EfD4bEcAQo1crUxF8p9clEuonQf.webp
:width: 1687px
:height: 781px
```
#### 开机启动
开机启动后,进入系统桌面。
```{image} images/image_NR7Wbect8o1MkSxtX9ncg4QFnHh.webp
:width: 720px
:height: 448px
```
### 功能使用
#### 触摸功能的使用方法
连接上 **MIPI** 显示器并且上电开机后,可以直接使用触摸功能。
#### 调整音频和显示
音频具体配置请依次进入:`Android 桌面 → 菜单 → 设置 → 提示音和震动`,即可对音频进行设置。
```{image} images/image_G08rbRvn9obJg4xFk3EcUO4Rnhe.webp
:width: 720px
:height: 448px
```
显示具体配置请依次进入:`Android 桌面 → 菜单 → 设置 → 显示` 即可对显示进行设置。
```{image} images/image_DSUDbWhR1o8Q4qxcNsxcyvP3nef.webp
:width: 720px
:height: 448px
```
> **注**:此处仅展示了屏幕和声音的操作图,像其他的电池、存储、壁纸、位置信息等操作步骤均与屏幕和声音的操作步骤一致。
### 视频播放功能
**Android桌面的视频播放功能**
1. 使用 **adb** 工具将视频文件推送到智能主控板。
```shell
adb push "E:\FeiShu\Animal World.mp4" /sdcard/DCIM #将相关视频推送到智能主控板SD卡的DCIM目录下#进入到智能主控板下
su #切换到root用户
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Animal World.mp4 #重新扫描媒体库的这个文件
```
1. 具体配置请依次进入:`Android 桌面 → 文件管理器 → 视频文件 → 播放视频`。
```{image} images/image_PSGMbdswYoKDR8xL6UZcKJxGn1p.webp
:width: 720px
:height: 448px
```
```{image} images/image_BaJIbdM43ovBfaxtk1QcN4USnWf.webp
:width: 720px
:height: 448px
```
### 音频播放功能
**Android桌面的音频播放功能**:
> **注**:播放音频需要在 **Quectel Pi M1 / L1** 智能主控板上将喇叭连接。
1. 使用 **adb** 工具将音频文件推送到智能主控板。
```shell
adb push E:\eage\test.mp3 /sdcard/Music #将音频推送到智能主控板SD卡的Music目录下#进入到智能主控板下
su #切换到root用户
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/Music/test.mp3 #重新扫描媒体库的这个文件
```
1. 具体配置请依次进入:`Android 桌面 → 文件管理器 → 播放音频`。
```{image} images/image_PYRbbBk0woFtQox4rM0cQtREn0c.webp
:width: 720px
:height: 448px
```
```{image} images/image_Lt8Db9wWcoUs1Dxs85HcnNK0nhc.webp
:width: 720px
:height: 448px
```
## HDMI
本节详细介绍了 **Quectel Pi M1 / L1** 智能主控板的 **Micro HDMI** 显示屏的使用方法,以 **10.1英寸HDMI** 显示屏为例进行详细说明。
### Micro HDMI屏幕连接
#### 硬件接口连接
```{image} images/image_FiQWbMTlao2ap2xt3HEcYHfUnMd.webp
:width: 1357px
:height: 1062px
```
#### 开机启动
开机启动后,进入系统桌面。
```{image} images/image_XbMQbL6Orofuu8xwMyKcLK6znDg.webp
:width: 720px
:height: 448px
```
### 功能使用
#### 触摸功能的使用方法
**HDMI** 显示器上电开机后,需要将 **USB** 口连接到 **HDMI** 屏幕。
> **注**:此功能需要带有触摸功能的 **HDMI** 屏幕。
按照下图进行连接:
```{image} images/image_W9QMb93ZwoGIkcxXf1NcvtxYnme.webp
:width: 802px
:height: 400px
```
#### 调整音频和显示
> **注**:接入 **HDMI** 显示器后,分辨率是自适应的,不可更改。目前所支持的 **HDMI** 分辨率是 **3840 × 2160**、**1920 × 1080**、**1280 × 720**、**720 × 480**
音频具体配置请依次进入:`Android 桌面 → 菜单 → 设置 → 提示音和震动`,即可对音频进行设置。
```{image} images/image_A9xebcKLpoSMPRxLJQUcHwNMnFb.webp
:width: 720px
:height: 448px
```
显示具体配置请依次进入:`Android 桌面 → 菜单 → 设置 → 显示`,即可对显示进行设置。
```{image} images/image_GgsqbQH7XonXxzx1JJJcGMVunSd.webp
:width: 720px
:height: 448px
```
### 视频播放功能
**Android桌面的视频播放功能**
1. 使用 **adb** 工具将视频文件推送到智能主控板。
```shell
adb push "E:\FeiShu\Animal World.mp4" /sdcard/DCIM #将相关视频推送到智能主控板SD卡的DCIM目录下#进入到智能主控板下su #切换到root用户
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Animal World.mp4 #重新扫描媒体库的这个文件
```
1. 具体配置请依次进入:`Android 桌面->文件管理器->视频文件->播放视频`。
```{image} images/image_MwYjbfh9Donnj3xwT19cEuXNnsd.webp
:width: 720px
:height: 448px
```
```{image} images/image_UkOSbK3BnoFMgoxKzCdc9zG2nfb.webp
:width: 720px
:height: 448px
```
### 音频播放功能
**Android桌面的音频播放功能**:
> **注**:播放音频需要在 **Quectel Pi M1 / L1** 智能主控板上将喇叭连接。
1. 使用 **adb** 工具将音频文件推送到智能主控板。
```plaintext
```SHELL
adb push E:\eage\test.mp3 /sdcard/Music #将音频推送到智能主控板SD卡的Music目录下
#进入到智能主控板下
su #切换到root用户
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/Music/test.mp3 #重新扫描媒体库的这个文件
```
```
2. 具体配置请依次进入:`Android 桌面->文件管理器->播放音频`。
```{image} images/image_BxWabw213o3Z7qxPRBccDibvn2w.webp
:width: 720px
:height: 448px
```
```{image} images/image_H1u2b799ioNlITxT5oHcGMQ7n3q.webp
:width: 720px
:height: 448px
```
### 进阶功能
#### HDMI屏幕的热插拔
**HDMI** 屏幕支持热插拔功能,可以在系统运行过程中插拔 **HDMI** 屏幕,系统会自动识别并显示画面。
#### 热插拔使用方法
1. 在系统运行状态下,直接将 **HDMI** 屏幕插入智能主控板的 **Micro HDMI** 接口。
2. 系统会自动检测到 **HDMI** 设备并启动显示输出。
3. 如需断开 **HDMI** 屏幕,可直接拔出 **Micro HDMI** 线缆,系统会自动切换显示输出。
#### 热插拔注意事项
1. 热插拔功能仅支持通过 **Micro HDMI** 接口连接的显示屏。
2. 使用DP转 **Micro HDMI** 适配器连接的屏幕可能无法正常支持热插拔。
3. 在插拔过程中请避免频繁操作,建议每次插拔间隔不少于5秒。
# 相机
本文档以 **IMX219** 摄像头模块为例,详细介绍了 **Quectel Pi M1 / L1** 智能主控板 **MIPI CSI** 摄像头接口的识别与连接方法,并提供了功能使用指导及常见问题解决方案。
## 硬件接口
**Quectel Pi M1 /L1** 智能主控板共支持两路摄像头 **MIPI CSI** 接口,如下图所示:
```{image} images/image_VskRbOT2bot57rxQPeRcH2dunWb.webp
:width: 2186px
:height: 1728px
:align: center
```
camera接口位置
## 快速开始
1.按照下图连接摄像头,连接时向上拉开连接器的黑色锁扣部分,将 **FPC** 排线插入第一个 **CSI** 卡槽,注意排线金手指朝向板内金属接触面,按下锁扣,确认 **FPC** 排线稳定没有松动。
```{image} images/image_EVPjbyuB1oZjcsx4v9ac1BIaneW.webp
:width: 674px
:height: 208px
```
连接camera的效果
2.参考显示连接显示屏
## 功能使用
### 预览
**IMX219** 摄像头所需的配置和参数已预先集成在内核中。具体操作步骤如下:摄像头连接至 **Quectel-Pi-M1 / L1**,`上电开机->Android桌面->相机->摄像头界面`。
```{image} images/image_AtZnb133eoFgNHx80KYccQPcnvh.webp
:width: 480px
:height: 856px
```
相机的位置
```{image} images/image_SVXMbbXZwo38hhxb8Agc1JnTnog.webp
:width: 480px
:height: 856px
```
摄像头界面
### 视频录制
具体操作步骤:`摄像头界面->点击 录制->开始录制视频->保存到相册`,录制的视频文件也可通过文件系统查找。
```{image} images/image_XLsxbb6jloyztGxAZvNccWODnVh.webp
:width: 480px
:height: 856px
```
录制界面预览
```{image} images/image_SiS7bwCbLoGuZ5xDAV7czqaenEf.webp
:width: 480px
:height: 856px
```
视频存放位置
### 拍照
具体操作步骤:`摄像头界面->点击 拍照->开始拍照->保存到相册`,照片文件也可通过文件系统查找。
```{image} images/image_KftbbLKiNopEfKxhTGMcPByAnCe.webp
:width: 480px
:height: 862px
```
拍照界面预览
```{image} images/image_N5f9bqUsDowyg4x1KvucWhtmnjd.webp
:width: 480px
:height: 856px
```
照片存放位置
# Wi-Fi/BT
**Quectel Pi M1 / L1** 智能主控板支持 **Wi-Fi** 和 **蓝牙 (BT)** 连接。用户需连接 **MIPI** 显示器,开机进入 **Android** 桌面后,选择目标网络或设备进行连接,以实现无线通信。
## Wi-Fi功能使用
### 启用并连接Wi-Fi
具体操作步骤如下:`连接MIPI显示器->上电开机->Android桌面->下拉桌面->点击Wi-Fi图标->进入Wi-Fi界面`。
```{image} images/image_XPOBbtw6ZoPhQ0xPx61cYq08n0g.webp
:width: 856px
:height: 480px
```
Wi-Fi打开的操作步骤
```{image} images/image_Q4wgbn9Z3o3eNlx0vdLcfGy4n3c.webp
:width: 856px
:height: 480px
```
选择Wi-Fi并连接
## 蓝牙功能使用
**Quectel Pi M1 / L1** 智能主控板内置蓝牙功能,支持连接蓝牙外设。
### 启用蓝牙
具体操作步骤如下:`连接MIPI显示器->上电开机->Android桌面->下拉桌面->点击BT图标->进入蓝牙(BT)界面`。
```{image} images/image_WxImbxohno1F9nxedfYcVasXnOn.webp
:width: 856px
:height: 460px
```
蓝牙打开的操作步骤
```{image} images/image_Fq0obW1txoSfiwxbiOtcgVYanSb.webp
:width: 856px
:height: 460px
```
选择对应蓝牙并连接
# 蜂窝网络
蜂窝网络(Cellular Network)是一种基于无线电通信的广域网络架构,广泛应用于移动通信和物联网设备。其核心特点是将覆盖区域划分为多个“蜂窝”(Cell),每个蜂窝由基站(Base Station)提供无线接入,终端设备通过基站连接到运营商核心网,实现语音、短信和数据业务。
本节主要针对 `USIM` 连接网络使用方法介绍。
> 支持双卡双待,本文档只针对USIM进行介绍。
仅支持4G LTE网络。
## 硬件连接
- SIM卡卡槽
Nano SIM拔插式卡座。
```{image} images/image_KnWEbyz6vo61Rhx6RuScnu9dnBd.webp
:width: 2186px
:height: 1728px
:align: center
```
- SIM卡天线接口
IPEX-4TH天线座。
```{image} images/image_UMUmbOvVloLusTxydIFc0ySLnUc.webp
:width: 600px
:height: 451px
```
## 快速开始
### 开机启动
进入系统,可查看SIM卡是否正常识别,正常识别如下图显示。
```{image} images/image_MTY1bOjGPoj3caxJzTicwmdtnDb.webp
:width: 960px
:height: 544px
```
### 联网确认
使用adb进入系统,ping 8.8.8.8确认网络是否连接正常。
```{image} images/image_GHXqbrF8Wo8f4Px5mLjcbmNMnLf.webp
:width: 569px
:height: 220px
```
## 功能使用
### 打开蜂窝网络
从屏幕顶部下滑,下拉菜单会显示 **Internet** 选项,点击选择对应LTE的切换开关,点亮成打开状态。
```{image} images/image_UrY5bAc1oopA8fxh8V2cvXL5nfd.webp
:width: 960px
:height: 554px
```
```{image} images/image_JRqebgpbjoJpDOxSesVcOCY0nsd.webp
:width: 905px
:height: 675px
```
### 关闭蜂窝网络
从屏幕顶部下滑,下拉菜单会显示 **Internet** 选项,点击选择对应LTE的切换开关,置暗成关闭状态。
```{image} images/image_PHoabLfGooiFQcxdSpncgv6enpg.webp
:width: 960px
:height: 554px
```
```{image} images/image_Q1cfbc91iojL2Yxe4wOcnW9Rntf.webp
:width: 905px
:height: 675px
```
## 常见问题
无线网卡激活成功,但是无法进行网络通信可能是什么原因?
一般有下面两种情况可能会导致这种问题:
**SIM卡欠费** 部分SIM卡在欠费状态下,模组仍能成功注册到网络并激活蜂窝无线网卡,但运营商会限制模组进行实际的网络通信。
**APN配置问题** SIM卡支持通过特定的APN接入到特殊的网络,并且用户配置的也是接入特殊网络的APN,此时设备接入的是特殊网络,并非互联网。用户可能在这种情况下进行了访问互联网的操作,那么就会出现访问失败的情况。具体APN配置请依次进入 `Settings -> Network & Internet -> Internet -> CMCC (connected/LTE) -> Access Point network` 后,进行配置新的APN进行网络连接。
# 以太网
**Quectel Pi M1 / L1** 智能主控板自带1个标准 **RJ45** 接口,支持10/100/1000 Mbps通信速率的以太网。
> 以太网对网线的要求:仅支持直连网线,不支持交叉网线。
## 硬件接口
```{image} images/image_OAhpb5uvaoZXcuxHMA0cPD8unpc.webp
:width: 2186px
:height: 1728px
:align: center
```
## 查看网络配置
系统默认开启DHCP自动获取网络配置。
终端输入 `ifconfig` 命令查看网络的接口地址:
```plaintext
ifconfig
```
输出示例:`inet addr:` 后面的 `192.168.90.116` 为本机的ip地址。
```{image} images/image_KtxIbMrkRo4p9fxriE3cEQo6nhb.webp
:width: 573px
:height: 585px
```
终端输入 `ip route` 命令查看网络路由信息:
```plaintext
ip route
```
输出示例:
```{image} images/image_KkHWbA28gouimSx8OUkc2LM6nAW.webp
:width: 492px
:height: 45px
```
查看 **DNS** 服务器地址:
```plaintext
dumpsys connectivity | grep -o ' DnsAddresses: \[[^]]*\]'
```
输出示例:
```{image} images/image_BDlFbu0FuoHPyKxpps6cyM8snZe.webp
:width: 514px
:height: 36px
```
## 测试网络
**执行`ping`****命令测试网络连接**
- 执行 `ping 8.8.8.8` 命令。如果能够成功 **ping** 通(接收到响应),则表示外网网络连接正常。
- 执行 `ping www.example.com` 命令。如果能够成功 **ping** 通,则表示外网网络连接正常且 **DNS** 解析功能正常。
# 音频功能
本文档提供音频系统操作的简明指南。系统采用 **ALSA + PulseAudio** 的双层音频架构,从底层驱动到应用层提供完整的音频解决方案。
硬件层面,系统支持:
- 外置麦克风输入(外接 **SH1.0-4P** 音频转耳机接口)
- 外置扬声器/耳机输出,可实现耳机播放与麦克风输入的全双工通信
软件层面,系统提供一组标准音频配置与调试命令,用于完成设备检查、录音、播放和音量控制等常见操作。
通过本文档,您无需深入复杂配置,即可快速定位并解决大多数音频相关问题。
## 硬件接口
- 外接 **SH1.0-4P** 音频转耳机接口
- 板载 **MEMS** 麦克风
```{image} images/image_Uf2BbggHSor7jtxJGvEckO8wnlk.webp
:width: 868px
:height: 443px
```
```{image} images/image_Dz5zbf3tco2Dayx2b71cHGZancc.webp
:width: 2186px
:height: 1728px
:align: center
```
## 查看声卡信息
可通过以下命令检查系统声卡与 PCM 设备的加载情况:
- 查看声卡列表:
```shell
cat /proc/asound/cards
```
```{image} images/image_XVAcbjIMgo7iqExpYQkci9h8nUb.webp
:width: 675px
:height: 86px
```
- 查看已分配的 **pcm** 流列表:
```shell
cat /proc/asound/pcm
```
```{image} images/image_KWpdbulLJo7AV3xYoHZcopeBnUt.webp
:width: 676px
:height: 362px
```
> 说明:若系统未检测到声卡信息或 **PCM 流**,则 **ALSA** 与 **PulseAudio** 均无法正常工作。
## 功能使用
### 音频录制
Android系统下录制
```shell
tinymix "TX DEC0 MUX" "SWR_MIC"
tinymix "ADC2 ChMap" "SWRM_TX2_CH1"
tinymix "TX SMIC MUX0" "SWR_MIC4"
tinymix "TX_AIF1_CAP Mixer DEC0" "1"
tinymix "ADC2_MIXER Switch" "1"
tinymix "ADC2 MUX" "INP3"
tinymix 'TX_AIF1_CAP Mixer DEC2' '1'
agmcap ./testMic3.wav -D 100 -d 101 -c 1 -r 48000 -i "CODEC_DMA-LPAIF_RXTX-TX-3"
```
结束录制
按下“**Ctrl**” + “**Z**”结束录音:
```{image} images/image_HNzzbhQCKoufcDxPKvNc3d8dnFf.webp
:width: 773px
:height: 367px
```
### 耳机麦克风录制
Android系统下录制
```shell
tinymix "TX DEC0 MUX" "SWR_MIC"
tinymix "TX SMIC MUX0" "SWR_MIC5"
tinymix "DEC0_BCS Switch" "1"
tinymix "TX_AIF1_CAP Mixer DEC0" "1"
tinymix "ADC2 ChMap" "SWRM_TX2_CH2"
tinymix "MBHC ChMap" "SWRM_TX3_CH3"
tinymix "ADC2_MIXER Switch" "1"
tinymix "ADC2 MUX" "INP2"
tinymix "BCS Channel" "CH10"
agmcap ./rec2.wav -D 100 -d 101 –c 1 -r 48000 -i "CODEC_DMA-LPAIF_RXTX-TX-3"
```
注意:操作时需要确保当前目录有写入权限。
```{image} images/image_CoypbKuOloj2FYxC3kTcy0fyn8g.webp
:width: 898px
:height: 644px
```
### 音频播放
板载扬声器未焊接,请使用外接耳机通道播放。
**耳机播放**
Android系统下播放(3.5 mm耳机)
```shell
adb root
adb shell
cd /data/local/tmp
tinymix "RX_MACRO RX0 MUX" "AIF1_PB"
tinymix "RX_MACRO RX1 MUX" "AIF1_PB"
tinymix "RX INT0_1 MIX1 INP0" "RX0"
tinymix "RX INT1_1 MIX1 INP0" "RX1"
tinymix "RX INT0 DEM MUX" "CLSH_DSM_OUT"
tinymix "RX INT1 DEM MUX" "CLSH_DSM_OUT"
tinymix "RX_COMP1 Switch" "1"
tinymix "RX_COMP2 Switch" "1"
tinymix "HPHL_RDAC Switch" "1"
tinymix "HPHR_RDAC Switch" "1"
agmplay ./vvdn_ring.wav -D 100 -d 100 -i CODEC_DMA-LPAIF_RXTX-RX-0 -dkv 0xA2000002
```
```{image} images/image_UufnbTY1Wo20YLxcVcFcKFUyn8d.webp
:width: 791px
:height: 374px
```
## 疑难解答
|
问题
|
可能原因
|
| --- | --- |
| 录音文件未生成 | - 检查设置指令是否正确。
- 操作目录是否有可读写权限。
|
| 音频播放失败 | - 检查播放文件是否存在。
- 检查播放指令是否正确,对于"-"需要注意中英文符号。
|
# USB
USB host是指能够控制和管理USB设备的系统或硬件,它负责为连接的USB设备(如U盘、键盘、鼠标等)提供电源、数据传输和管理功能。
Quectel Pi M1/L1智能主控板自带2个标准USB 3.0 Type-A接口、2个标准USB 2.0 Type-A接口和1个USB 3.1 Type-C接口。USB 2.0接口最高数据传输速率可达480 Mbps,USB 3.1接口最高数据传输速率可达5 Gbps。
```{image} images/image_RdJTbA2u4olnT8xTxuhcumM5n3c.webp
:width: 883px
:height: 583px
:align: center
```
## 功能使用
### 接入U盘
在Type-A口中插入一个U盘。
```{image} images/image_RoxbbeF5gobDysxYb5YcqTdrnAd.webp
:width: 2127px
:height: 1280px
:align: center
```
通知栏显示USB设备。
```{image} images/image_JPKrbOXcpoSPMexyK4OcRoF0nSf.webp
:width: 1275px
:height: 797px
```
下拉并展开通知栏。
```{image} images/image_LHIVb7QuSoMpgyxzfEkc7tTsnud.webp
:width: 1277px
:height: 798px
```
点击 `EXPLORE` 按钮浏览U盘内容。
```{image} images/image_YqJubfisooaEP6xMdMqcjLHqnib.webp
:width: 1275px
:height: 797px
```
# SD卡
**SD** 卡 (**Secure Digital Card**)是一种广泛使用的便携式存储介质,具备体积小、容量大、功耗低及易用性强等特点。它常用于手机、相机、嵌入式设备及单板计算机,用于存储系统文件、应用程序及数据,是现代移动与嵌入式设备中最主流的存储方案之一。
## 硬件接口
**Quectel Pi M1/L1** 智能主控板配备了一个 **micro SD + Nano SIM** 二合一卡座,支持 **SD 3.0** 协议。
注意:上方为 **micro SD** 卡座,下方是 **Nano SIM** 卡座。
```{image} images/image_DVQobigh3oA7FxxePWIcomPTnUh.webp
:width: 2186px
:height: 1728px
:align: center
```
## 功能使用
### 系统识别
插入 **SD** 卡后,可通过以下命令确认系统是否识别到设备:
```bash
fdisk -l
```
对比SD卡插入前后的输出信息,可以看到系统已成功识别SD卡:
```{image} images/image_IqNHb8P8LofuLKxAXM0c6XoLnnb.webp
:width: 954px
:height: 417px
```
### 挂载与卸载
查看挂载路径:
```plaintext
df -h
```
如下所示,设备已自动挂载至 `/mnt/sdcard`:
```{image} images/image_NHErbqXCXoMegxxNj6LciKCTnKc.webp
:width: 699px
:height: 448px
```
查看挂载目录下的文件:
```plaintext
ls /mnt/sdcard
```
挂载到其他目录:
```bash
sudo mkdir -p /media/sdcard
sudo mount /dev/mmcblk1p1 /media/sdcard
```
查看挂载结果:
```bash
ls /media/sdcard
```
使用完毕后卸载:
```bash
sudo umount /media/sdcard
```
> 提示:卸载后再拔出SD卡可防止数据损坏。
### 文件操作与权限
普通用户访问 **SD** 卡时,可能遇到权限不足的问题。可通过以下命令更改挂载点的所有权:
```bash
sudo chown /mnt/sdcard
sudo chmod 755 /mnt/sdcard
```
将 ```` 替换为当前登录的用户名。修改后即可在 `/mnt/sdcard` 目录中正常读写文件。
# ADC
ADC 子系统由信号复用器和电压模数转换器(VADC)组成,用于将内部及外部模拟信号通过多路复用方式路由至 VADC,并完成模拟量到数字量的转换。
## ADC模块硬件架构图
```{image} images/image_E6xcbplvroHQV6xvbdmc0ThFngh.webp
:width: 834px
:height: 446px
```
## M1模块支持使用的ADC
模块支持使用的ADC如下:
|
接口
|
引脚号
|
引脚名
|
PM4250引脚号
|
电压域
|
MPM中断
|
引脚描述
|
| --- | --- | --- | --- | --- | --- | --- |
| ADC | 128 | ADC0 | GPIO4 | 1.8 V | - | PMU_GPIO04 |
| ADC | 185 | ADC1 | GPIO7 | 1.8 V | - | PMU_GPIO07 |
### ADC位置
智能主控板上ADC接口的位置如下图所示:
```{image} images/image_RajibxoWCofhUFxiKBgcU301nxe.webp
:width: 1397px
:height: 911px
```
智能主控板上ADC的位置
### ADC节点使用示例
ADC节点主要用来查看该节点输入的ADC数值。
#### 使用方法
测试时使用可调直流电源作为输入信号源:
- 将电源 **正极** 连接到 **ADC0** 或 **ADC1** 引脚。
- 电源 **GND** 与智能主控板 **GND** 相连。
各通道对应关系及输入特性如下:
- **ADC0**:
- 连接到GPIO4
- 节点:`in_voltage_adc_gpio4_input`
- 支持输入电压范围:约 **0~5.25 V**
- **ADC1**:
- 连接到GPIO7
- 节点:`in_voltage_adc_gpio7_input`
- 支持输入电压范围:约 **0~5.25 V**
设置直流电源输出为目标电压后,可通过以下命令读取 ADC 采样结果(单位:μV):
```bash
adb shell
cd /sys/bus/iio/devices/iio:device0
cat in_voltage_adc_gpio4_input
cat in_voltage_adc_gpio7_input
```
```{image} images/image_GX5mbAqFLoNhbSx7S7ccuzJkn2Y.webp
:width: 711px
:height: 458px
```
ADC读取示例输出
#### 测试代码
以下示例代码演示了在应用程序中读取 ADC 电压值的基本方法。程序通过访问ADC对应的节点文件,读取ADC原始值(单位为 μV),并转换为整数以供后续处理。
点击展开/折叠:ADC读取测试代码(可自行NDK编译)
```c
#include #include #include #include #include #include int main()
{
int fd = -1;
int adc_value;
char buff[64] = {0};
// 打开ADC节点文件(ADC0对应GPIO4)
fd = open("/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio4_input", O_RDONLY);
if(fd < 0){
perror("open error\n");
return fd;
}
// 读取ADC值int n_read = read(fd, buff, sizeof(buff));
if(n_read < 0) {perror("read error\n");close(fd);return -1;}// 将读取的字符串转换为整数(单位为μV)
adc_value = atol(buff);
printf("read %d bytes, content: %s\n", n_read, buff);
printf("ADC value: %d μV\n", adc_value);
close(fd);
return 0;
}
```
**代码说明:**
- 使用 `open()` 函数打开ADC节点文件 `/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio4_input`。
- 使用 `read()` 函数读取ADC原始值(字符串格式,单位为μV)。
- 使用 `atol()` 函数将字符串转换为长整型数值。
- 读取的ADC值单位为微伏(μV),可根据需要转换为电压值。
**编译和运行:**
```bash
# 使用NDK编译# 将编译好的可执行文件推送到设备并运行
adb push test_adc /data/local/tmp/test_adc
adb shell "chmod +x /data/local/tmp/test_adc"
adb shell "/data/local/tmp/test_adc"
```
### 测试数据
#### ADC引脚规格
模块上的ADC引脚定义如下:
|
接口
|
模块引脚号
|
模块引脚名
|
模块 I/O
|
NET名称(复用功能)
|
说明
|
| --- | --- | --- | --- | --- | --- |
| ADC | 128 | ADC0 | I | ADC0 | 1) 连接器输入电压范围:0–5.25 V。
2) 分压电阻:RH = 18 kΩ,RL = 10 kΩ。
3) ADC引脚电压 = 连接器输入电压(J0403)× (10/28)。
4) 示例:输入 3 V 时,ADC引脚电压应为 3 V × 10/28 ≈ 1.0714 V。 |
| ADC | 185 | ADC1 | I | ADC1 | 与ADC0相同的电气特性(0–5.25 V输入范围,RH = 18 kΩ,RL = 10 kΩ,ADC引脚电压 = Vin × 10/28)。 |
#### ADC0测试数据
|
电压源
|
ADC0(μV)
|
理论值(V)
|
| --- | --- | --- |
| 1.8 V | 638408 | 1.8 V×10/28 = 0.642857 |
| 3 V | 1066414 | 3 V×10/28 = 1.07143 |
| 5 V | 1784623 | 5 V×10/28 = 1.78571 |
#### ADC1测试数据
|
电压源
|
ADC1(μV)
|
理论值(V)
|
| --- | --- | --- |
| 1.8 V | 640549 | 1.8 V×10/28 = 0.642857 |
| 3 V | 1069269 | 3 V×10/28 = 1.07143 |
| 5 V | 1789814 | 5 V×10/28 = 1.78571 |
## L1模块支持使用的ADC
```{image} images/image_HkgLbuD3WoNFyUxMhaBc3LNrnSd.webp
:width: 834px
:height: 446px
```
|
接口
|
引脚号
|
引脚名
|
PM4250引脚号
|
电压域
|
MPM中断
|
引脚描述
|
| --- | --- | --- | --- | --- | --- | --- |
| ADC | 128 | ADC0 | GPIO4 | 1.8 V | - | PMU_GPIO04 |
### ADC位置
智能主控板上ADC接口的位置如下图所示:
```{image} images/image_C0IGbmjcHoGLwVxjQgYcXcgHn7d.webp
:width: 1397px
:height: 911px
```
智能主控板上ADC的位置
### ADC节点使用示例
ADC节点主要用来查看该节点输入的ADC数值。
### 使用方法
测试时使用可调直流电源作为输入信号源:
- 将电源 **正极** 连接到 **ADC0** 或 **ADC1** 引脚。
- 电源 **GND** 与智能主控板 **GND** 相连。
各通道对应关系及输入特性如下:
- **ADC0**:
- 连接到GPIO4
- 节点:`in_voltage_adc_gpio6_input`
- 支持输入电压范围:约 **0~5.25 V**
设置直流电源输出为目标电压后,可通过以下命令读取 ADC 采样结果(单位:μV):
```bash
adb shell
cd /sys/bus/iio/devices/iio:device0
cat in_voltage_adc_gpio6_input
```
ADC读取示例输出
### 测试代码
以下示例代码演示了在应用程序中读取 ADC 电压值的基本方法。程序通过访问ADC对应的节点文件,读取ADC原始值(单位为 μV),并转换为整数以供后续处理。
点击展开/折叠:ADC读取测试代码(可自行NDK编译)
```c
#include #include #include #include #include #include int main()
{
int fd = -1;
int adc_value;
char buff[64] = {0};
// 打开ADC节点文件(ADC0对应GPIO4)
fd = open("/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio6_input", O_RDONLY);
if(fd < 0){
perror("open error\n");
return fd;
}
// 读取ADC值int n_read = read(fd, buff, sizeof(buff));
if(n_read < 0) {perror("read error\n");close(fd);return -1;}// 将读取的字符串转换为整数(单位为μV)
adc_value = atol(buff);
printf("read %d bytes, content: %s\n", n_read, buff);
printf("ADC value: %d μV\n", adc_value);
close(fd);
return 0;
}
```
**代码说明:**
- 使用 `open()` 函数打开ADC节点文件 `/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio6_input`。
- 使用 `read()` 函数读取ADC原始值(字符串格式,单位为μV)。
- 使用 `atol()` 函数将字符串转换为长整型数值。
- 读取的ADC值单位为微伏(μV),可根据需要转换为电压值。
**编译和运行:**
```bash
# 使用NDK编译# 将编译好的可执行文件推送到设备并运行
adb push test_adc /data/local/tmp/test_adc
adb shell "chmod +x /data/local/tmp/test_adc"
adb shell "/data/local/tmp/test_adc"
```
### 测试数据
#### ADC引脚规格
模块上的ADC引脚定义如下:
|
接口
|
模块引脚号
|
模块引脚名
|
模块 I/O
|
NET名称(复用功能)
|
说明
|
| --- | --- | --- | --- | --- | --- |
| ADC | 128 | ADC0 | I | ADC0 | 1) 连接器输入电压范围:0–5.25 V。
2) 分压电阻:RH = 18 kΩ,RL = 10 kΩ。
3) ADC引脚电压 = 连接器输入电压(J0403)× (10/28)。
4) 示例:输入 3 V 时,ADC引脚电压应为 3 V × 10/28 ≈ 1.0714 V。 |
#### ADC0测试数据
|
电压源
|
ADC0(μV)
|
理论值(V)
|
| --- | --- | --- |
| 1.8 V | 638408 | 1.8 V×10/28 = 0.642857 |
| 3 V | 1066414 | 3 V×10/28 = 1.07143 |
| 5 V | 1784623 | 5 V×10/28 = 1.78571 |
# 40‑Pin扩展
Quectel Pi M1智能主控板提供了标准的40‑pin GPIO扩展接口,支持GPIO、I2C、SPI、UART、PWM等多种外设接口。下面将介绍如何测试这些接口的功能。
## 引脚定义
```{image} images/image_SVr0b975QoCafLxH3Dscu5R3nBg.webp
:width: 1185px
:height: 767px
```
M1GPIO引脚对应列表
```{image} images/image_GEsybnPttoMlrNxpVcfckCu6nld.webp
:width: 1819px
:height: 548px
```
L1GPIO引脚对应列表
```{image} images/image_YTHibu1pDoCYF9x9Q6Dc4xAUnpf.webp
:width: 1845px
:height: 489px
:align: center
```
## GPIO测试
本节以测试M1 40‑Pin的pin7引脚为例,演示如何使用GPIO功能。pin7对应的gpio_num为83。
L1的GPIO测试可复用代码,但是需要在代码中修改对应引脚对应的GPIO值
### 硬件连接
**连接方法一:万用表测量电压**
将pin7(GPIO_83)引脚接万用表正极,pin6(GND)引脚接万用表负极。通过万用表可以测量引脚输出的电压值,验证GPIO功能是否正常。
**连接方法二:使用GPIO扩展板指示灯**
也可以通过接插 **树莓派4B/3B GPIO扩展板** 到40-Pin扩展接口来测试GPIO高低电平。该扩展板将各GPIO引脚引出并配有对应的指示灯,可将待测GPIO引脚配置为输出并将其接到扩展板上,当GPIO输出高电平时,小灯点亮,输出低电平时,小灯熄灭,从而直观观察GPIO电平变化是否符合预期。
关于该扩展板的更多信息,可参考配件章节中的 **GPIO terminal expansion board** 条目。
```{image} images/image_PopvbZgT0oEhhgxcMRNctchznMg.webp
:width: 978px
:height: 606px
```
GPIO扩展板指示灯示例
### 测试方法
以下示例基于Android平台,需要使用NDK对C代码进行交叉编译后才可使用
pin_control_sysfs源码(可自行使用NDK编译)
```c
#include #include #include #include #include #include // GPIO引脚映射表 (SC200U Gamma Linux项目)typedef struct {int pin;int gpio_num; // Linux sysfs GPIO编号} pin_map_t;// 根据Bengal平台的GPIO映射 (与Android项目一致)static const pin_map_t pin_mapping[] = {{3, 494}, {5, 495}, {7, 481}, {8, 467}, {10, 468}, {11, 504}, {12, 496}, {13, 429},{15, 469}, {16, 482}, {18, 423}, {19, 399}, {21, 398}, {22, 478}, {23, 400}, {24, 401},{26, 480}, {27, 412}, {28, 413}, {29, 403}, {31, 402}, {32, 465}, {33, 396}, {35, 497},{36, 414}, {37, 415}, {38, 499}, {40, 498}};#define PIN_COUNT (sizeof(pin_mapping) / sizeof(pin_map_t))/**
* 根据pin号获取GPIO映射信息
*/const pin_map_t* get_pin_info(int pin) {for (int i = 0; i < PIN_COUNT; i++) {if (pin_mapping[i].pin == pin) {return &pin_mapping[i];}}return NULL;}/**
* 导出GPIO到sysfs
*/int export_gpio(int gpio_num) {char path[64];int fd;// 检查GPIO是否已导出snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d", gpio_num);if (access(path, F_OK) == 0) {return 0; // 已导出}// 导出GPIO
fd = open("/sys/class/gpio/export", O_WRONLY);if (fd < 0) {printf("无法打开 /sys/class/gpio/export: %s\n", strerror(errno));return -1;}char gpio_str[8];snprintf(gpio_str, sizeof(gpio_str), "%d", gpio_num);if (write(fd, gpio_str, strlen(gpio_str)) < 0) {// 如果该GPIO已被导出,写入时会返回错误,忽略此非致命问题if (errno != EBUSY) {printf("无法export GPIO%d: %s\n", gpio_num, strerror(errno));close(fd);return -1;}}close(fd);usleep(100000); // 延时100ms,等待sysfs节点创建完成return 0;}/**
* 取消导出GPIO
*/int unexport_gpio(int gpio_num) {int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);if (fd < 0) {return -1;}char gpio_str[8];snprintf(gpio_str, sizeof(gpio_str), "%d", gpio_num);write(fd, gpio_str, strlen(gpio_str));close(fd);return 0;}/**
* 设置GPIO方向
*/int set_gpio_direction(int gpio_num, const char *direction) {char path[64];int fd;snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio_num);
fd = open(path, O_WRONLY);if (fd < 0) {printf("无法打开 %s: %s\n", path, strerror(errno));return -1;}if (write(fd, direction, strlen(direction)) < 0) {printf("设置GPIO%d方向失败: %s\n", gpio_num, strerror(errno));close(fd);return -1;}close(fd);return 0;}/**
* 设置GPIO值
*/int set_gpio_value(int gpio_num, int value) {char path[64];int fd;snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio_num);
fd = open(path, O_WRONLY);if (fd < 0) {printf("无法打开 %s: %s\n", path, strerror(errno));return -1;}char value_str[2];snprintf(value_str, sizeof(value_str), "%d", value);if (write(fd, value_str, 1) < 0) {printf("设置GPIO%d值失败: %s\n", gpio_num, strerror(errno));close(fd);return -1;}close(fd);return 0;}/**
* 读取GPIO值
*/int get_gpio_value(int gpio_num, int *value) {char path[64];int fd;char val[2];snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio_num);
fd = open(path, O_RDONLY);if (fd < 0) {printf("无法打开 %s: %s\n", path, strerror(errno));return -1;}if (read(fd, val, 1) < 0) {printf("读取GPIO%d失败: %s\n", gpio_num, strerror(errno));close(fd);return -1;}close(fd);*value = (val[0] == '1') ? 1 : 0;return 0;}/**
* 使用sysfs控制GPIO
* action: 0=拉低, 1=拉高, 2=读取
*/int control_gpio_sysfs(int gpio_num, int action) {// 确保GPIO已导出if (export_gpio(gpio_num) < 0) {return -1;}if (action == 0 || action == 1) {// 设置方向为输出if (set_gpio_direction(gpio_num, "out") < 0) {return -1;}// 设置值if (set_gpio_value(gpio_num, action) < 0) {return -1;}printf("✓ GPIO%d已设置为%s电平\n", gpio_num, action ? "高" : "低");// 验证设置是否生效usleep(50000);int verify_value;if (get_gpio_value(gpio_num, &verify_value) == 0) {printf(" 验证: GPIO%d实际状态: %s电平 (值: %d)\n",
gpio_num, verify_value ? "高" : "低", verify_value);}} else if (action == 2) {// 读取GPIO值int value;if (get_gpio_value(gpio_num, &value) == 0) {printf("✓ GPIO%d当前状态: %s电平 (值: %d)\n",
gpio_num, value ? "高" : "低", value);} else {return -1;}}return 0;}/**
* 读取所有引脚状态
*/int read_all_pins() {printf("========================================\n");printf("=== 读取所有引脚状态 ===\n");printf("========================================\n");int success_count = 0;for (int i = 0; i < PIN_COUNT; i++) {printf("pin%d (GPIO%d): ", pin_mapping[i].pin, pin_mapping[i].gpio_num);fflush(stdout);if (control_gpio_sysfs(pin_mapping[i].gpio_num, 2) == 0) {
success_count++;} else {printf("✗ 读取失败\n");}}printf("========================================\n");printf("成功读取 %d/%lu 个引脚\n", success_count, PIN_COUNT);printf("========================================\n");return 0;}/**
* 控制所有引脚
*/int control_all_pins(int action) {printf("========================================\n");printf("开始控制所有引脚为%s电平...\n", action ? "高" : "低");printf("========================================\n");int success_count = 0;for (int i = 0; i < PIN_COUNT; i++) {printf("pin%d (GPIO%d): ", pin_mapping[i].pin, pin_mapping[i].gpio_num);fflush(stdout);if (control_gpio_sysfs(pin_mapping[i].gpio_num, action) == 0) {
success_count++;} else {printf("✗ 控制失败\n");}}printf("========================================\n");printf("成功控制 %d/%lu 个引脚\n", success_count, PIN_COUNT);printf("========================================\n");return (success_count == PIN_COUNT) ? 0 : 1;}/**
* 显示帮助信息
*/void show_help(const char *prog_name) {printf("用法: %s \n", prog_name);printf("例如: %s pin3 up # 拉高pin3\n", prog_name);printf("例如: %s pin5 down # 拉低pin5\n", prog_name);printf("例如: %s pin7 get # 读取pin7\n", prog_name);printf("例如: %s all up # 拉高所有pin\n", prog_name);printf("例如: %s all down # 拉低所有pin\n", prog_name);printf("例如: %s all get # 读取所有pin状态\n", prog_name);printf("\n支持的引脚:\n");for (int i = 0; i < PIN_COUNT; i++) {printf("pin%d(GPIO%d) ", pin_mapping[i].pin, pin_mapping[i].gpio_num);if ((i + 1) % 4 == 0) printf("\n");}printf("\n");}/**
* 主函数
*/int main(int argc, char *argv[]) {if (argc < 3) {show_help(argv[0]);return 1;}char *pin_str = argv[1];char *action_str = argv[2];// 解析动作参数int action;if (strcmp(action_str, "up") == 0) {
action = 1;} else if (strcmp(action_str, "down") == 0) {
action = 0;} else if (strcmp(action_str, "get") == 0) {
action = 2;} else {printf("错误: 动作必须是up, down或get\n");return 1;}// 处理all命令if (strcmp(pin_str, "all") == 0) {if (action == 2) {return read_all_pins();} else {return control_all_pins(action);}}// 解析引脚号int pin;if (sscanf(pin_str, "pin%d", &pin) != 1) {printf("错误: 无效的引脚格式,请使用pin3, pin5等\n");return 1;}const pin_map_t *pin_info = get_pin_info(pin);if (pin_info == NULL) {printf("错误: 不支持的引脚 pin%d\n", pin);return 1;}printf("控制 pin%d (GPIO%d)...\n", pin, pin_info->gpio_num);return control_gpio_sysfs(pin_info->gpio_num, action);}
```
将交叉编译后可执行文件推送到开发板并赋权:
```bash
adb push <本地存放路径>/pin_control_sysfs /data/local/tmp/pin_control_sysfs
adb shell "chmod +x /data/local/tmp/pin_control_sysfs"
adb shell "ls -lh /data/local/tmp/pin_control_sysfs"
```
进入设备端shell并运行工具以查看用法:
```bash
adb shell
cd /data/local/tmp
./pin_control_sysfs
```
运行后会打印可用命令和已支持的40‑pin引脚映射,示例输出如下:
```{image} images/image_MaFJbuvqkoRmdfxSIiucg8ocnAe.webp
:width: 575px
:height: 330px
```
3. 常用操作示例:
- 拉高单个引脚:`./pin_control_sysfs pin7 up`
- 拉低单个引脚:`./pin_control_sysfs pin7 down`
- 读取单个引脚:`./pin_control_sysfs pin7 get`
- 操作全部引脚:`./pin_control_sysfs all up | all down | all get`
> 提示:运行时确保目标引脚未被其它功能(如I2C/SPI/UART)占用,建议先通过扩展板指示灯或万用表确认电平变化是否符合预期。
## I2C测试
40‑pin接口的pin3和pin5默认为I2C的数据和时钟引脚。为了测试I2C接口,我们需要外接一个I2C设备。此处我们选用微雪环境传感器扩展板,对应的设备节点为 `/dev/i2c-1`。
### 测试准备
| **
IC
** | **
I2C Addr
** | **
ID Reg
** | **
期望ID返回值
** |
| --- | --- | --- | --- |
|
WSL25911FN
|
0x29
|
0x12
|
0x50
|
|
BME280
|
0x76
|
0xD0
|
0x60
|
|
MPU9250
|
0x68
|
0x75
|
0x71
|
|
LTR390-UV-1
|
0x53
|
0x06
|
0xB2
|
PS:此处的WSL25911FN在读ID寄存器的时候需要带上command bit,也就是0xA0,即寄存器 0x12 实际访问时常写成 0xA0 | 0x12 = 0xB2,其他的不用。
本次测试使用微雪环境传感器扩展板,通过40‑pin接口进行连接。
**硬件连接示意图:**
```{image} images/image_Zhm2bFko3oRYhhxQ6kVcK8ggnMb.webp
:width: 1189px
:height: 639px
```
接入扩展板的Quectel Pi M1
### 测试方法
```plaintext
adb shell #进入ADB shell
ls -al /dev/i2c* #查看I2C-1是否在对应目录下
i2cdetect -y 1 #查看对应的I2C一共挂了多少个设备
```
```{image} images/image_VKP4bz27doI6CAxVVatcyXrLniG.webp
:width: 526px
:height: 339px
:align: center
```
### I2C读取/写入
```plaintext
i2cget -y 1 0x53 0x05 b #读取i2c1下设备地址为0x53的0x05寄存器的值,大小为一个byte
i2cset -y 1 0x53 0x05 0x03 b #设置i2c1下设备地址为0x53的0x05寄存器的值,大小为一个byte
```
```{image} images/image_ROZdb1zFaoG9LrxHhdCcrbuXnlb.webp
:width: 364px
:height: 112px
:align: center
```
## SPI测试
40-pin接口中的SPI功能对应的设备片选节点为 `/dev/spidev0.0` 和 `/dev/spidev0.1`。在adb中输入以下内容可查看这些节点:
```bash
adb shell "ls /dev/spidev*"
```
### 微雪OLED显示器测试(cs0和cs1)
请按照以下表格进行接线:
|
OLED引脚
|
应该连接到
|
SC200U物理引脚
|
备注
|
| --- | --- | --- | --- |
| VCC | 3.3V电源 | Pin1或Pin17 | 必须是3.3V |
| GND | 地线 | Pin6/9/14等任意GND | 共地 |
| DIN/MOSI | SPI数据 | Pin19 | SPI_MOSI |
| CLK/SCK | SPI时钟 | Pin23 | SPI_CLK |
| CS/CE | 片选 | Pin24/Pin26 | SPI_CE0/SPI_CE1 |
| D/C | 数据/命令 | Pin22 | GPIO80 |
| RES/RST | 复位 | Pin18 | GPIO25 |
**硬件连接示意图:**
```{image} images/image_Fwf8bsi5Yofjoax5Ad5cmHr0nPd.webp
:width: 1137px
:height: 645px
```
SPI0.0 (CE0, Pin24) 测试 - OLED显示 "ANDROID SPI0.0"
```{image} images/image_MVUjbjOjyoJRRrxl1ekcdPa7nAc.webp
:width: 1176px
:height: 646px
```
SPI0.1 (CE1, Pin26) 测试 - OLED显示 "ANDROID SPI0.1"
**执行步骤:**
创建源文件
新建 `test_spi.c` 文件,内容如下:
test_spi源码(可自行使用NDK或交叉编译)
```c
/*
* Test both CE0 and CE1 - requires manual wire switching
* This helps verify which CS pin your OLED is connected to
* Enhanced with text display and FPS performance test
*/#include #include #include #include #include #include #include #include #include #define GPIO_BASE 398#define GPIO_RST (GPIO_BASE + 25)#define GPIO_DC (GPIO_BASE + 80)#define SPI_MODE SPI_MODE_3#define SPI_SPEED 8000000#define OLED_WIDTH 128#define OLED_HEIGHT 32#define OLED_PAGES 4// Complete 5x7 fontstatic const uint8_t font_5x7[][5] = {{0x00, 0x00, 0x00, 0x00, 0x00}, // Space{0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0{0x00, 0x42, 0x7F, 0x40, 0x00}, // 1{0x42, 0x61, 0x51, 0x49, 0x46}, // 2{0x21, 0x41, 0x45, 0x4B, 0x31}, // 3{0x18, 0x14, 0x12, 0x7F, 0x10}, // 4{0x27, 0x45, 0x45, 0x45, 0x39}, // 5{0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6{0x01, 0x71, 0x09, 0x05, 0x03}, // 7{0x36, 0x49, 0x49, 0x49, 0x36}, // 8{0x06, 0x49, 0x49, 0x29, 0x1E}, // 9{0x7E, 0x11, 0x11, 0x11, 0x7E}, // A{0x7F, 0x49, 0x49, 0x49, 0x36}, {0x3E, 0x41, 0x41, 0x41, 0x22}, // B C{0x7F, 0x41, 0x41, 0x22, 0x1C}, {0x7F, 0x49, 0x49, 0x49, 0x41}, // D E{0x7F, 0x09, 0x09, 0x09, 0x01}, {0x3E, 0x41, 0x49, 0x49, 0x7A}, // F G{0x7F, 0x08, 0x08, 0x08, 0x7F}, {0x00, 0x41, 0x7F, 0x41, 0x00}, // H I{0x20, 0x40, 0x41, 0x3F, 0x01}, {0x7F, 0x08, 0x14, 0x22, 0x41}, // J K{0x7F, 0x40, 0x40, 0x40, 0x40}, {0x7F, 0x02, 0x0C, 0x02, 0x7F}, // L M{0x7F, 0x04, 0x08, 0x10, 0x7F}, {0x3E, 0x41, 0x41, 0x41, 0x3E}, // N O{0x7F, 0x09, 0x09, 0x09, 0x06}, {0x3E, 0x41, 0x51, 0x21, 0x5E}, // P Q{0x7F, 0x09, 0x19, 0x29, 0x46}, {0x46, 0x49, 0x49, 0x49, 0x31}, // R S{0x01, 0x01, 0x7F, 0x01, 0x01}, {0x3F, 0x40, 0x40, 0x40, 0x3F}, // T U{0x1F, 0x20, 0x40, 0x20, 0x1F}, {0x3F, 0x40, 0x38, 0x40, 0x3F}, // V W{0x63, 0x14, 0x08, 0x14, 0x63}, {0x07, 0x08, 0x70, 0x08, 0x07}, // X Y{0x61, 0x51, 0x49, 0x45, 0x43}, // Z{0x08, 0x08, 0x08, 0x08, 0x08}, // -{0x00, 0x36, 0x36, 0x00, 0x00}, // :{0x00, 0x60, 0x60, 0x00, 0x00}, // .};static int char_to_index(char c) {if (c == ' ') return 0;if (c >= '0' && c <= '9') return 1 + (c - '0');if (c >= 'A' && c <= 'Z') return 11 + (c - 'A');if (c >= 'a' && c <= 'z') return 11 + (c - 'a');if (c == '-') return 37;if (c == ':') return 38;if (c == '.') return 39;return 0;}int gpio_write(int gpio, int value);void oled_quick_test(const char *device, const char *name);int main() {printf("\n╔════════════════════════════════════════════════════════╗\n");printf("║ Chip Select Pin Comparison Tool ║\n");printf("╚════════════════════════════════════════════════════════╝\n");printf("\nThis tool helps identify which CS pin is connected.\n");printf("\nInstruction:\n");printf(" 1. Connect OLED CS to Pin24 first\n");printf(" 2. Run test - should see display\n");printf(" 3. Move OLED CS to Pin26\n");printf(" 4. Run test again - should see display\n");printf("\n");char choice;printf("Which pin is your OLED CS currently connected to?\n");printf(" [0] Pin24 (CE0)\n");printf(" [1] Pin26 (CE1)\n");printf("Choice: ");scanf(" %c", &choice);if (choice == '0') {printf("\nTesting CE0 (Pin24)...\n");oled_quick_test("/dev/spidev0.0", "CE0-PIN24");} else if (choice == '1') {printf("\nTesting CE1 (Pin26)...\n");oled_quick_test("/dev/spidev0.1", "CE1-PIN26");} else {printf("Invalid choice\n");return 1;}return 0;}void oled_quick_test(const char *device, const char *name) {// Display bufferstatic uint8_t buffer[OLED_PAGES][OLED_WIDTH];// Simple GPIO functionsint gpio_export(int gpio) {int fd = open("/sys/class/gpio/export", O_WRONLY);if (fd < 0) return -1;char buf[10];snprintf(buf, sizeof(buf), "%d", gpio);write(fd, buf, strlen(buf));close(fd);usleep(100000);return 0;}int gpio_set_dir(int gpio) {char path[50];snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio);int fd = open(path, O_WRONLY);if (fd < 0) return -1;write(fd, "out", 3);close(fd);return 0;}int gpio_write(int gpio, int value) {char path[50];snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);int fd = open(path, O_WRONLY);if (fd < 0) return -1;char buf[2] = {value ? '1' : '0', 0};write(fd, buf, 1);close(fd);return 0;}void send_cmd(int fd, uint8_t cmd) {gpio_write(GPIO_DC, 0);write(fd, &cmd, 1);usleep(1000);}void set_pixel(int x, int y, int color) {if (x >= 0 && x < OLED_WIDTH && y >= 0 && y < OLED_HEIGHT) {int page = y / 8;int bit = y % 8;if (color)
buffer[page][x] |= (1 << bit);else
buffer[page][x] &= ~(1 << bit);}}void draw_char(int x, int y, char c) {int idx = char_to_index(c);for (int col = 0; col < 5; col++) {uint8_t data = font_5x7[idx][col];for (int row = 0; row < 8; row++) {if (data & (1 << row)) {set_pixel(x + col, y + row, 1);}}}}void draw_text(int x, int y, const char *text) {int cursor = x;while (*text) {draw_char(cursor, y, *text);
cursor += 6;
text++;}}void clear_buffer() {memset(buffer, 0, OLED_PAGES * OLED_WIDTH);}void display_buffer(int fd) {for (int page = 0; page < OLED_PAGES; page++) {send_cmd(fd, 0xB0 + page);send_cmd(fd, 0x04);send_cmd(fd, 0x10);gpio_write(GPIO_DC, 1);write(fd, buffer[page], OLED_WIDTH);}}// Initialize GPIOgpio_export(GPIO_RST);gpio_export(GPIO_DC);usleep(200000);gpio_set_dir(GPIO_RST);gpio_set_dir(GPIO_DC);// Open SPIprintf(" Opening %s...\n", device);int spi_fd = open(device, O_RDWR);if (spi_fd < 0) {perror(" ERROR: Cannot open device");printf(" → %s is NOT working or OLED not connected\n", name);return;}printf(" ✓ Device opened\n");// Configure SPIuint8_t mode = SPI_MODE;uint32_t speed = SPI_SPEED;ioctl(spi_fd, SPI_IOC_WR_MODE, &mode);ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);// Reset OLEDprintf(" Resetting OLED...\n");gpio_write(GPIO_RST, 0);usleep(50000);gpio_write(GPIO_RST, 1);usleep(50000);// Initialize OLEDprintf(" Initializing OLED...\n");send_cmd(spi_fd, 0xAE);send_cmd(spi_fd, 0x04); send_cmd(spi_fd, 0x10); send_cmd(spi_fd, 0x40);send_cmd(spi_fd, 0x81); send_cmd(spi_fd, 0xFF);send_cmd(spi_fd, 0xA1); send_cmd(spi_fd, 0xA6);send_cmd(spi_fd, 0xA8); send_cmd(spi_fd, 0x1F);send_cmd(spi_fd, 0xC8);send_cmd(spi_fd, 0xD3); send_cmd(spi_fd, 0x00);send_cmd(spi_fd, 0xD5); send_cmd(spi_fd, 0xF0);send_cmd(spi_fd, 0xD8); send_cmd(spi_fd, 0x05);send_cmd(spi_fd, 0xD9); send_cmd(spi_fd, 0xC2);send_cmd(spi_fd, 0xDA); send_cmd(spi_fd, 0x12);send_cmd(spi_fd, 0xDB); send_cmd(spi_fd, 0x08);send_cmd(spi_fd, 0xAF);printf(" ✓ OLED initialized\n");// Test 1: Display "USING: CE0" or "USING: CE1"printf("\n [Test 1] Displaying chip select info...\n");clear_buffer();draw_text(10, 4, "USING:");if (strstr(name, "CE0")) {draw_text(25, 16, "CE0");} else {draw_text(25, 16, "CE1");}display_buffer(spi_fd);printf(" ✓ Screen showing: USING %s\n", strstr(name, "CE0") ? "CE0" : "CE1");sleep(3);// Test 2: Fill whiteprintf("\n [Test 2] Filling screen WHITE...\n");memset(buffer, 0xFF, OLED_PAGES * OLED_WIDTH);display_buffer(spi_fd);printf(" ✓ Screen should be WHITE\n");sleep(2);// Test 3: Blink testprintf("\n [Test 3] Blink test (5 times)...\n");for (int i = 0; i < 5; i++) {// Whitememset(buffer, 0xFF, OLED_PAGES * OLED_WIDTH);display_buffer(spi_fd);usleep(200000);// Blackmemset(buffer, 0x00, OLED_PAGES * OLED_WIDTH);display_buffer(spi_fd);usleep(200000);printf(" Blink %d/5\n", i + 1);}printf(" ✓ Blink test complete\n");// Test 4: FPS Performance Testprintf("\n [Test 4] FPS Performance Test...\n");printf(" Running 100 frame updates...\n");struct timespec start, end;clock_gettime(CLOCK_MONOTONIC, &start);for (int frame = 0; frame < 100; frame++) {// Generate test patternfor (int page = 0; page < OLED_PAGES; page++) {for (int x = 0; x < OLED_WIDTH; x++) {
buffer[page][x] = (x + frame) & 0xFF;}}display_buffer(spi_fd);}clock_gettime(CLOCK_MONOTONIC, &end);double elapsed = (end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1000000000.0;double fps = 100.0 / elapsed;printf(" ✓ Performance: %.1f FPS\n", fps);// Display FPS result on screenclear_buffer();draw_text(10, 4, "FPS TEST");char fps_str[20];snprintf(fps_str, sizeof(fps_str), "%.1f FPS", fps);draw_text(20, 16, fps_str);display_buffer(spi_fd);printf(" ✓ FPS result displayed on screen\n");sleep(3);// Test 5: CS Pin Functionality Testprintf("\n [Test 5] CS Pin Functionality Test...\n");printf(" This test verifies CS pin actually controls the device\n");// Step 1: Display number 1printf(" Step 1: Displaying pattern 1...\n");clear_buffer();draw_text(15, 4, "PATTERN");draw_text(50, 16, "1");display_buffer(spi_fd);sleep(2);// Step 2: Try to update through the OTHER CS (should fail if CS works)printf(" Step 2: Trying to update via OTHER CS...\n");const char *other_device = strstr(name, "CE0") ? "/dev/spidev0.1" : "/dev/spidev0.0";const char *other_name = strstr(name, "CE0") ? "CE1" : "CE0";int other_fd = open(other_device, O_RDWR);if (other_fd >= 0) {uint8_t mode = SPI_MODE;uint32_t speed = SPI_SPEED;ioctl(other_fd, SPI_IOC_WR_MODE, &mode);ioctl(other_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);// Try to display pattern 2 via other CSclear_buffer();draw_text(15, 4, "PATTERN");draw_text(50, 16, "2");for (int page = 0; page < OLED_PAGES; page++) {send_cmd(other_fd, 0xB0 + page);send_cmd(other_fd, 0x04);send_cmd(other_fd, 0x10);gpio_write(GPIO_DC, 1);write(other_fd, buffer[page], OLED_WIDTH);}close(other_fd);printf(" Sent update via %s (other CS)\n", other_name);sleep(2);printf("\n ╔════════════════════════════════════════════════╗\n");printf(" ║ CHECK SCREEN: Still showing '1' or changed to '2'? ║\n");printf(" ╚════════════════════════════════════════════════╝\n");printf("\n If still showing '1': CS pin is WORKING correctly ✓\n");printf(" If changed to '2': CS pin NOT working (both CS active) ✗\n\n");sleep(2);}// Step 3: Update back via correct CSprintf(" Step 3: Updating via correct CS (%s)...\n", name);clear_buffer();draw_text(15, 4, "BACK TO");draw_text(50, 16, "1");display_buffer(spi_fd);printf(" ✓ Should see pattern 1 again\n");sleep(2);// Test 6: FPS result summaryprintf("\n [Test 6] Final Summary...\n");clear_buffer();draw_text(30, 4, name);char summary_str[20];snprintf(summary_str, sizeof(summary_str), "%.1fFPS", fps);draw_text(20, 16, summary_str);display_buffer(spi_fd);printf(" ✓ Summary displayed\n");sleep(3);// Clear screenclear_buffer();display_buffer(spi_fd);close(spi_fd);printf("\n======================================================================\n");printf(" %s Test Complete!\n", name);printf("======================================================================\n");printf(" CS Pin Test Result:\n");printf(" If screen didn't change to '2' in Step 2:\n");printf(" → CS pin is working correctly ✓\n");printf(" If screen changed to '2':\n");printf(" → CS pin might not be controlling the device ✗\n");printf(" → Both CS pins may be active simultaneously\n");printf("\n Performance: %.1f FPS\n", fps);printf("======================================================================\n");}
```
```bash
adb root
adb push "D:\tools\40pin android\test_android_spi" /data/local/tmp/test_android_spi
adb shell "chmod +x /data/local/tmp/test_android_spi"
adb shell
cd /data/local/tmp/
./test_android_spi
```
程序运行后会提示选择SPI设备:
- **选择0**:使用 `/dev/spidev0.0` (CE0, Pin24)
- **选择1**:使用 `/dev/spidev0.1` (CE1, Pin26)
请根据实际接线情况选择对应的设备(CS/CE引脚连接到pin24则选择0,连接到pin26则选择1)。
**测试结果:**
程序执行后会依次进行以下测试:
```plaintext
╔══════════════════════════════════════════════════════╗
║ Android SPI OLED 测试程序 ║
║ 适配Android设备 ║
╚══════════════════════════════════════════════════════╝
选择SPI设备:
[0] /dev/spidev0.0 (CE0, Pin24)
[1] /dev/spidev0.1 (CE1, Pin26)
[c] 自定义设备路径
选择: 1
使用设备: /dev/spidev0.1
初始化GPIO...
✓ GPIO初始化成功
打开SPI设备: /dev/spidev0.1
✓ SPI设备已打开并配置
- 模式: 3
- 速度: 2000000 Hz
- 位宽: 8 bits
初始化OLED...
复位OLED...
发送初始化命令...
✓ OLED初始化完成
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
开始测试序列: SPI0.1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[测试1] 显示设备信息...
✓ 屏幕应显示: ANDROID SPI0.1
[测试2] 全白屏测试...
✓ 屏幕应全白
[测试3] 闪烁测试 (5次)...
闪烁 1/5
闪烁 2/5
闪烁 3/5
闪烁 4/5
闪烁 5/5
✓ 闪烁测试完成
[测试4] FPS性能测试...
运行100帧更新...
✓ 性能: 40.3 FPS (耗时: 2.48秒)
✓ FPS结果已显示
[测试5] 图案测试...
图案1: 横条纹
图案2: 竖条纹
图案3: 棋盘格
✓ 图案测试完成
[测试6] 测试总结...
✓ 总结已显示
```
**测试说明:**
- 测试1:验证OLED能够正常显示文本信息
- 测试2:验证OLED全屏显示功能
- 测试3:通过闪烁测试验证OLED刷新功能
- 测试4:测试SPI通信性能(FPS值)
- 测试5:测试不同图案显示功能
- 测试6:显示测试总结信息
若所有测试项均显示 √ 标记,则表明SPI通信与OLED功能一切正常。
---
## UART测试
在 **40-pin** 接口中,**pin8** 和 **pin10** 默认配置为 **UART** 功能,对应的设备节点为 **/dev/ttyHS0**。
**查看串口设备**
可以使用以下命令查看系统中所有的串口设备:
```bash
ls /dev/tty*
```
### UART回环测试
本测试通过将 **pin8** 和 **pin10** 短接,验证串口收发功能是否正常。
**硬件连接:** 将40‑pin的pin8(TX)和pin10(RX)短接。
**执行步骤:** 创建源文件
新建 `uart_loopback.c` 文件,内容如下:
uart_loopback源码(可自行使用NDK或交叉编译)
```c
#include #include #include #include #include #include #include #include // 全局变量用于信号处理static int serial_fd = -1;static volatile int running = 1;// 信号处理函数void signal_handler(int sig) {printf("\n用户中断测试\n");
running = 0;if (serial_fd >= 0) {close(serial_fd);printf("串口已关闭\n");}exit(0);}// 配置串口参数int configure_serial(int fd, int baudrate) {struct termios tty;if (tcgetattr(fd, &tty) != 0) {printf("获取串口属性失败: %s\n", strerror(errno));return -1;}// 设置波特率speed_t speed;switch (baudrate) {case 9600: speed = B9600; break;case 19200: speed = B19200; break;case 38400: speed = B38400; break;case 57600: speed = B57600; break;case 115200: speed = B115200; break;case 230400: speed = B230400; break;default: speed = B115200; break;}cfsetospeed(&tty, speed);cfsetispeed(&tty, speed);// 配置串口参数
tty.c_cflag &= ~PARENB; // 无奇偶校验
tty.c_cflag &= ~CSTOPB; // 1个停止位
tty.c_cflag &= ~CSIZE; // 清除数据位设置
tty.c_cflag |= CS8; // 8个数据位
tty.c_cflag &= ~CRTSCTS; // 禁用硬件流控制
tty.c_cflag |= CREAD | CLOCAL; // 启用接收和本地连接// 配置输入模式
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控制
tty.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式// 配置输出模式
tty.c_oflag &= ~OPOST; // 原始输出// 配置本地模式
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式// 设置超时
tty.c_cc[VTIME] = 10; // 1秒超时
tty.c_cc[VMIN] = 0; // 非阻塞读取if (tcsetattr(fd, TCSANOW, &tty) != 0) {printf("设置串口属性失败: %s\n", strerror(errno));return -1;}return 0;}// UART回环测试函数int serial_loopback_test(const char* port, int baudrate) {char test_data[] = "Hello, Serial Loopback!";char received_data[256];ssize_t bytes_written, bytes_read;// 打开串口设备
serial_fd = open(port, O_RDWR | O_NOCTTY | O_SYNC);if (serial_fd < 0) {printf("无法打开串口 %s: %s\n", port, strerror(errno));return -1;}// 配置串口if (configure_serial(serial_fd, baudrate) != 0) {close(serial_fd);return -1;}printf("串口 %s 已打开,开始回环测试(按Ctrl+C退出)...\n", port);while (running) {// 清空接收缓冲区tcflush(serial_fd, TCIOFLUSH);// 发送数据
bytes_written = write(serial_fd, test_data, strlen(test_data));if (bytes_written < 0) {printf("发送数据失败: %s\n", strerror(errno));break;}printf("发送: %s\n", test_data);// 等待数据接收usleep(100000); // 100ms// 读取回环数据
bytes_read = read(serial_fd, received_data, sizeof(received_data) - 1);if (bytes_read < 0) {printf("读取数据失败: %s\n", strerror(errno));break;}
received_data[bytes_read] = '\0'; // 添加字符串结束符// 验证结果if (bytes_read == strlen(test_data) &&
strncmp(received_data, test_data, strlen(test_data)) == 0) {printf("接收: %s → 测试通过\n\n", received_data);} else {printf("接收异常: 发送[%zu] vs 接收[%zd] → 测试失败\n",
strlen(test_data), bytes_read);if (bytes_read > 0) {printf("接收内容: %s\n\n", received_data);} else {printf("未接收到数据\n\n");}}sleep(1); // 间隔1秒重复测试}close(serial_fd);printf("串口 %s 已关闭\n", port);return 0;}int main(int argc, char* argv[]) {const char* port = "/dev/ttyHS0"; // 默认串口int baudrate = 115200; // 默认波特率// 解析命令行参数if (argc >= 2) {
port = argv[1];}if (argc >= 3) {
baudrate = atoi(argv[2]);}// 设置信号处理signal(SIGINT, signal_handler);signal(SIGTERM, signal_handler);printf("UART回环测试程序\n");printf("使用串口: %s\n", port);printf("波特率: %d\n", baudrate);printf("=====================================\n");// 执行回环测试int result = serial_loopback_test(port, baudrate);return result;}
```
**执行步骤:**
```bash
adb root
adb push <本地路径>/uart_loopback_test /data/local/tmp/uart_loopback
adb shell chmod +x /data/local/tmp/uart_loopback
adb shell ls -la /data/local/tmp/
adb shell
cd /data/local/tmp/
./uart_loopback
```
**测试结果:**
当pin8和pin10正确短接时,程序会持续发送数据并验证接收的数据是否一致:
```plaintext
UART回环测试程序
使用串口: /dev/ttyHS0
波特率: 115200
=====================================
串口 /dev/ttyHS0 已打开,开始回环测试(按Ctrl+C退出)...
发送: Hello, Serial Loopback!
接收: Hello, Serial Loopback! → 测试通过
发送: Hello, Serial Loopback!
接收: Hello, Serial Loopback! → 测试通过
发送: Hello, Serial Loopback!
接收: Hello, Serial Loopback! → 测试通过
```
按 `Ctrl+C` 可退出测试程序。程序会自动关闭串口并退出。
# 散热器控制
## 温控风扇原理
后台进程检测/sys/class/thermal/thermal_zone*
并轮读几个核的温度取最高,并且根据最高温度来配置风扇的PWM占空比。
|
温度
|
占空比/255
|
| --- | --- |
|
<30℃
|
0
|
|
30~40℃
|
64
|
|
40~50℃
|
128
|
|
50~60℃
|
192
|
|
>60℃
|
255
|
| **风扇引脚** | **M1开发板引脚(40PIN)** |
| --- | --- |
| 红线(电源正极) | PIN2 |
| 黑线(电源负极) | PIN5 |
| 蓝线(PWM) | PIN33 |
```{image} images/image_E7PFbFE7hoYzQWxStjOcFUWnnEI.webp
:width: 2304px
:height: 1728px
:align: center
```
## 测试流程
默认风扇功能是关闭的,开机后输入对应指令查看风扇服务的状态
```plaintext
/system/bin/sh /vendor/bin/40Pin-ctrl fan status #查看当前风扇服务的状态,未启用风扇服务为disabled,启用为enable
/system/bin/sh /vendor/bin/40Pin-ctrl fan enable #使能风扇服务,使能后可以查看下状态是否为enable
/system/bin/sh /vendor/bin/40Pin-ctrl fan disable #关闭风扇服务,停止测试才需要输入该指令
```
在第一个终端里面输入
```plaintext
/system/bin/sh /vendor/bin/40Pin-ctrl fan status
/system/bin/sh /vendor/bin/40Pin-ctrl fan enable
#SC200U
watch -n 1 'PWM=/sys/class/pwm/pwmchip0/pwm0; p=$(cat "$PWM/period"); d=$(cat "$PWM/duty_cycle"); echo $(( (d * 255 + p / 2) / p ))'
#SC200E
watch -n 1 'PWM=/sys/bus/platform/devices/1c40000.qcom,spmi:qcom,pm2250@1:qcom,pwms@be00/pwm/pwmchip2/pwm0; p=$(cat "$PWM/period"); d=$(cat "$PWM/duty_cycle"); echo $(( (d * 255 + p / 2) / p ))'
```
开第二个终端,做升温
```plaintext
for i in 1 2 3 4; do
yes > /dev/null &
done
```
如果第3步里面的升温不够可以把负载调大
```plaintext
for i in $(seq 1 $(nproc)); do
yes > /dev/null &
done
```
看两个终端里面的温度和风扇的PWM占空比是不是和表里面的对齐
```{image} images/image_TfC8bmNR9oCOmCxnS6LckH0cnqd.webp
:width: 1021px
:height: 263px
:align: center
```
测试结束后停止负载
```plaintext
killall yes
```
# 应用场景
- **媒体娱乐终端**: 使用 **Quectel Pi M1** 智能主控板作为媒体娱乐终端,可以播放视频、音乐、查看图片等。
- **工业数据可视化平台**: 使用 **Quectel Pi M1** 智能主控板作为工业数据可视化平台,可以显示工业数据、监控设备运行情况、进行数据处理等。
- **智能交互终端**:使用 **Quectel Pi M1** 智能主控板作为智能交互终端,可以使用于各类的人机交互应用。
# 疑难解答
## 如何查看系统日志
查看系统日志,需要将串口线接至电脑,并使用串口工具查看日志。
## 黑屏问题
在正常连接好设备后,开机后显示 **黑屏**,可能是以下的情况:
**电源连接问题:**
检查显示屏的供电接口是否正确连接,确认4个供电接口中至少有一个已正确连接,确保供电螺丝已拧紧,顶针与40-pin接口接触良好。
**连接线路问题:**
检查MIPI排线是否正确插入并牢固连接,确认排线方向是否正确,避免插反,检查连接器是否有物理损坏。
**系统启动问题:**
确认系统已正常启动并加载显示服务,查看系统日志确认显示驱动是否正常加载。
## 花屏问题
在正常连接好设备后,开机后显示 **花屏**,可能是以下的情况:
**信号干扰:**
检查MIPI排线是否过长或质量不佳,确认排线周围是否有强电磁干扰源,尝试更换高质量的MIPI排线。
**硬件兼容性问题:**
确认使用的显示屏型号与 **Quectel Pi M1** 完全兼容,检查显示屏固件版本是否为最新版本。
## 触摸功能异常
触摸功能异常,可能是以下情况:
**驱动问题:**
确认系统已正确加载触摸屏驱动,检查I2C通信是否正常建立。