day1 环境搭建|底层驱动
今日任务
Ubuntu 环境配置、SSH 连接开发板、Qt 交叉编译搭建
V4L2/ALSA/UART/GPIO 硬件调试、4 个底层 Demo、TCP/UDP 基础、多线程模板编写
今日必背 3 题 + 通用技能知识
- 交叉编译概念、嵌入式为何必须使用交叉编译?
- /dev、/sys、/proc 三个文件系统作用区别?
- 多线程互斥锁与条件变量解决的核心问题?
- Linux 基础:常用文件 / 目录操作命令、权限 chmod/chown、SSH 远程连接原理
- C++ 基础:变量、数据类型、指针基础概念
当日交付
交叉编译环境、四大硬件 Demo、多线程通用模板
小时拆分
09:00--10:30 Ubuntu 配置、SSH 连 T113-S3、环境变量
10:30--12:00 Qt 交叉编译工具链部署、开发板设备节点熟悉
14:00--15:30 V4L2/ALSA/UART/GPIO 硬件调试
15:30--17:00 写 4 个底层 Demo、TCP/UDP 基础代码
17:00--18:00 Linux 多线程、互斥锁、条件变量模板编写
19:00-20:30 常用文件 / 目录操作命令、权限 chmod/chown、SSH 远程连接原理、变量、数据类型、指针基础概念
21:00--21:30 记踩坑笔记
一、Ubuntu 配置、SSH 连 T113-S3、环境变量的踩坑点
1.1Ubuntu的ens33未启动
解决方法
- 配置/etc/netplan/01-network-manager-all.yaml
- 保存退出后执行sudo netplan apply---预期结果无反应重新刷新终端
- ip route show default---预期结果default via [自己的网关] dev ens33 proto static--这里以小米路由器的网关为例
其中 nano /etc/netplan/01-network-manager-all.yaml时可能会出现版本不兼容语法空格有问题等错误配置的时候一定要手敲空格*2不要按tab键
通用版yaml但是这里为了便于观看对其使用的是tab键
network:
version: 2
renderer: networkd
ethernets:
ens33:
addresses:
- 192.168.31.100/24[你自己配置的静态ip]
gateway4: 192.168.31.1[自己的网关]
nameservers:
addresses:
192.168.31.1[自己的网关]
8.8.8.8
1.2SSH配置
问题描述:
---串口连接使用失败
---查看板子发现存在sshd
---尝试启动sshd失败
解决方法
覆盖原SSH 服务端的加密密钥文件后重新输入执行命令。
生成 RSA 密钥
ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ""
生成 ECDSA 密钥
ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N ""
生成 ED25519 密钥
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
1.3 No route to host的原因
排查步骤:
在虚拟中ping开发板 --- 显示ping不通
在Windows的cmd中ping开发板 --显示ping不通
----确认原因----同网段但 ping 不通,说明开发板和路由器的连接有问题,或者路由器开启了 AP 隔离。
解决步骤:
1.在开发板上ping路由官网
通过---开发板能上网,但路由器限制了设备间互访--this
不通过--开发板的 WiFi 连接异常,需要重新连接 WiFi
尝试解决步骤:
添加中间服务器Ngrok
1.2板子启动速度慢---tf卡抢时间因为内存大
解决方法
在env.txt内写入如下代码--适配新手
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p1 rootwait rw bootcmd=run bootfromsd; run bootfromemmc; bootfromsd=setenv root /dev/mmcblk0p1; load mmc 0 0x41000000 zImage; load mmc 0 0x42000000 board.dtb; bootz 0x41000000 - 0x42000000; bootfromemmc=setenv root /dev/mmcblk1p1; load mmc 1 0x41000000 zImage; load mmc 1 0x42000000 board.dtb; bootz 0x41000000 - 0x42000000; bootdelay=3
1.3挂载上tf卡后的开机启动文件
#!/bin/sh
echo "------run rc.modules file-----"
cp /opt/ip /sbin
#export boot_partition=$(fw_printenv -n boot_partition 2>/dev/null)
if [ x${boot_partition} == xboot ];then
/sbin/insmod /lib/modules/5.4.61/usb-storage.ko
/sbin/insmod /lib/modules/5.4.61/sunxi_gpadc.ko
/sbin/insmod /lib/modules/5.4.61/gt9xxnew_ts.ko
echo 0x07090160 0x083F10F7 > /sys/class/sunxi_dump/write
/sbin/insmod /lib/modules/5.4.61/8723ds.ko
/sbin/insmod /lib/modules/5.4.61/8189fs.ko
/sbin/insmod /lib/modules/5.4.61/r8188eu.ko
ifconfig wlan0 up
sleep 1
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf -Dwext &
udhcpc -i wlan0
wpa_supplicant -Dnl80211 -iwlan0 -c /etc/wifi/wpa_supplicant.conf -B
amixer -D hw:audiocodec cset name='Headphone Switch' 1 && \
amixer -D hw:audiocodec cset name='Headphone volume' 25
ifconfig eth0 up
ip link set can0 up type can bitrate 500000
led flash
cd /opt
./led_flash &
cd /
run lvgl demo
lv_examples 0 &
export LD_LIBRARY_PATH='/usr/lib/arm-qt/lib:/usr/lib/arm-qt/plugins/platforms:/usr/lib/eyesee-mpp::/usr/lib/arm-qt/lib'
export OLDPWD='/root'
export PATH='/usr/sbin:/usr/bin:/sbin:/bin'
export PS1='\u@\h:\w\$ '
export PWD='/etc/init.d'
export QTDIR='/usr/lib/arm-qt'
export QT_BOOT='/usr/lib/arm-qt'
export QT_PLUGIN_PATH='/usr/lib/arm-qt/plugins'
export QT_QPA_FONTDIR='/usr/lib/arm-qt/lib/fonts'
export QT_QPA_GENERIC_PLUGINS='tslib:/dev/input/event2'
export QT_QPA_PLATFORM='linuxfb:tty=/dev/fb0'
export QT_QPA_PLATFORM_PLUGIN_PATH='/usr/lib/arm-qt/plugins'
export SHELL='/bin/sh'
export SHLVL='2'
export TERM='vt102'
export TSLIB_CALIBFILE='/etc/pointercal'
export TSLIB_CONFFILE='/etc/ts.conf'
export TSLIB_CONSOLEDEVICE='none'
export TSLIB_FBDEVICE='/dev/fb0'
export TSLIB_PLUGINDIR='/usr/lib/ts'
export TSLIB_TSDEVICE='/dev/input/event2'
export USER='root'
export boot_type='5'
export bt_mac=''
export disp_reserve='2457600,0x43f19000'
export mac_addr=''
export mbr_offset='1032192'
export partitions='mbr@ubi0_0:boot-resource@ubi0_1:env@ubi0_2:env-redund@ubi0_3:boot@ubi0_4:rootfs@ubi0_5:private@ubi0_6:rootfs_data@ubi0_7:UDISK@ubi0_8:'
export snum=''
export specialstr=''
export uboot_message='2018.05-g2a1965a-dirty(07/10/2022-02:50:57)'
export wifi_mac=''
echo 5555 > /sys/class/android_usb/android0/enable
/usr/bin/adbd &
fi
三、V4L2/ALSA/UART/GPIO 硬件调试
| 外设 | 引脚 | 功能 | Linux 设备 / 内核号 |
|---|---|---|---|
| 指纹模块 UART2 | TX: PE2RX: PE3 | 串口收发 | /dev/ttyS2 |
| 三色 LED 补光 | 红: PE0绿: PE1蓝: PE4 | GPIO 输出 | 内核号:红: 128绿: 129蓝: 132 |
| 继电器 | PE5 | GPIO 输出 | 内核号:133 |
| USB 摄像头 | USB 口 1 | 视频采集 | /dev/video0 |
| USB 音响 | USB 口 2 | 音频输出 | plughw:1,0 |
| ADB 调试 | Type-C1 | OTG/ADB | adb shell |
| 串口调试 | Type-C2 | UART 日志 | /dev/ttyS3 |
说明:全志 T113-S3 的 GPIO 内核号计算方式:
组号*32 + 引脚号,PE 组为第 4 组,因此:
- PE0 = 4*32 + 0 = 128
- PE1 = 4*32 + 1 = 129
- PE2 = 4*32 + 2 = 130
- PE3 = 4*32 + 3 = 131
- PE4 = 4*32 + 4 = 132
- PE5 = 4*32 + 5 = 133
4 个硬件底层 Demo(V4L2/ALSA/UART/GPIO)
1. GPIO 底层 Demo(LED + 继电器)
gpio_demo.c
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define LED_R "/sys/class/gpio/gpio128/value"
#define LED_G "/sys/class/gpio/gpio129/value"
#define LED_B "/sys/class/gpio/gpio132/value"
#define RELAY "/sys/class/gpio/gpio133/value"
// 写 GPIO
void gpio_write(const char *path, int val) {
int fd = open(path, O_WRONLY);
dprintf(fd, "%d", val);
close(fd);
}
int main() {
printf("GPIO 底层 Demo\n");
// 红灯亮
gpio_write(LED_R, 1);
sleep(1);
gpio_write(LED_R, 0);
// 绿灯亮
gpio_write(LED_G, 1);
sleep(1);
gpio_write(LED_G, 0);
// 继电器开门 1 秒
gpio_write(RELAY, 1);
sleep(1);
gpio_write(RELAY, 0);
return 0;
}
UART 底层 Demo(指纹模块 UART2)
uart_demo.c
cpp
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
int uart_open(const char *dev) {
int fd = open(dev, O_RDWR | O_NOCTTY);
struct termios opt;
tcgetattr(fd, &opt);
// 9600 8N1
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
opt.c_cflag |= CS8;
opt.c_cflag &= ~PARENB;
opt.c_cflag &= ~CSTOPB;
tcsetattr(fd, TCSANOW, &opt);
return fd;
}
int main() {
int fd = uart_open("/dev/ttyS2");
char buf[32];
// 发指令
write(fd, "CHECK_FP\n", 8);
printf("发送指纹查询指令\n");
// 读数据
read(fd, buf, sizeof(buf));
printf("收到:%s\n", buf);
close(fd);
return 0;
}
V4L2 底层 Demo(摄像头采集一帧)
v4l2_demo.c
cpp
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main() {
int fd = open("/dev/video0", O_RDWR);
struct v4l2_capability cap;
ioctl(fd, VIDIOC_QUERYCAP, &cap);
printf("摄像头:%s\n", cap.card);
close(fd);
return 0;
}
ALSA 底层 Demo(播放音频)
alsa_demo.c
cpp
#include <stdio.h>
#include <alsa/asoundlib.h>
int main() {
snd_pcm_t *handle;
int ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
printf("ALSA 打开状态:%d\n", ret);
snd_pcm_close(handle);
return 0;
}
TCP / UDP 网络 Demo(可远程控制门禁)
1. TCP 服务端 Demo
tcp_server.c
cpp
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {
AF_INET, htons(8080), INADDR_ANY
};
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 5);
printf("TCP 监听 8080\n");
int client_fd = accept(server_fd, NULL, NULL);
char buf[16];
read(client_fd, buf, sizeof(buf));
printf("收到:%s\n", buf);
close(client_fd);
close(server_fd);
return 0;
}
UDP Demo
udp_demo.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr = {AF_INET, htons(8080), INADDR_ANY};
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
char buf[16];
recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
printf("UDP 收到:%s\n", buf);
close(fd);
return 0; }
多线程模板(门禁必备:视频 + 指纹 + 网络并行)
thread_demo.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 线程1:摄像头预览
void *cam_thread(void *arg) {
while (1) { printf("摄像头运行...\n"); sleep(1); } }
// 线程2:指纹识别
void *fp_thread(void *arg) {
while (1) { printf("指纹监听...\n"); sleep(1); }
}
// 线程3:TCP网络控制
void *tcp_thread(void *arg) {
while (1) { printf("网络监听...\n"); sleep(2); }
}
int main() {
pthread_t t1, t2, t3;
pthread_create(&t1, NULL, cam_thread, NULL);
pthread_create(&t2, NULL, fp_thread, NULL);
pthread_create(&t3, NULL, tcp_thread, NULL);
pthread_join(t1, NULL);
return 0;
}
编译命令(Tina ADB)
编译 GPIO
gcc gpio_demo.c -o gpio
编译 UART
gcc uart_demo.c -o uart
编译 V4L2
gcc v4l2_demo.c -o v4l2
编译 ALSA
gcc alsa_demo.c -o alsa -lasound
编译 TCP/UDP
gcc tcp_server.c -o tcp
gcc udp_demo.c -o udp
编译多线程
gcc thread_demo.c -o thread -pthread
第 2 天 人脸识别|指纹解析|QtUI
今日任务
OpenCV 嵌入式移植、V4L2 视频采集、Haar 人脸检测实现
指纹 UART 协议解析、Qt 门禁主界面开发、多线程解耦防 UI 卡顿
门禁数据库建模:设计管理员表、用户表字段结构,定义主键、账号唯一约束
今日必背 3 题+必备通用技能
- Qt 信号槽原理,跨线程操作 UI 的禁忌?
- V4L2 摄像头完整采集流程?
- 串口通信如何处理丢包、数据异常问题?
- Linux:进程查看 ps、kill、后台运行 &、nohup
- C++:引用、指针与引用区别
当日交付
人脸检测 Demo、指纹识别 Demo、Qt 门禁基础 UI、门禁管理员 + 用户表建表语句
一、 OpenCV 嵌入式移植
二、V4L2 视频采集
三、Haar 人脸检测实现
四、指纹 UART 协议解析
五、Qt 门禁主界面开发
六、多线程解耦防 UI 卡顿
七、交付物
人脸检测 Demo
指纹识别 Demo
Qt 门禁基础 UI
门禁管理员 + 用户表建表语句
门禁数据库建模:设计管理员表、用户表字段结构,定义主键、账号唯一约束
数据库选型:SQLite3 轻量嵌入式数据库 原因:无服务、单文件、占用资源极小、适配 Linux 嵌入式、掉电不损坏、无需部署,完美适配 T113-S3 硬件资源。核心实体:用户表、指纹表、人脸信息表、开门记录表、系统日志表、管理员表关系:管理员 --- 管理 --- 用户;用户 --- 绑定 --- 指纹 / 人脸;用户 --- 生成 --- 开门记录;系统自动生成 --- 日志
数据库描述
- 数据库:SQLite3
- 选型理由:轻量无服务、单文件存储、占用内存极小、适配嵌入式 Linux、无需安装部署、断电安全、支持事务,非常适合 T113-S3 资源受限的嵌入式设备。
- 核心实体表:
- 管理员表 admin
- 用户表 user
- 人脸信息表 face_info
- 指纹信息表 fingerprint
- 开门记录表 open_record
- 系统日志表 system_log
门禁 SQLite3 实体关系
- 管理员 (1) → 管理 → 多用户 (多) 一对多
- 用户 (1) → 绑定 → 单条人脸信息 (1) 一对一
- 用户 (1) → 绑定 → 单条指纹信息 (1) 一对一
- 用户 (1) → 产生 → 多条开门记录 (多) 一对多
- 系统自动生成 → 多条系统日志
建表
sql
-- 管理员表
CREATE TABLE admin (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 用户表
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_name TEXT NOT NULL,
phone TEXT,
password TEXT NOT NULL,
status INTEGER DEFAULT 1,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 人脸信息表
CREATE TABLE face_info (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER UNIQUE NOT NULL,
face_feature BLOB,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES user(id)
);
-- 指纹信息表
CREATE TABLE fingerprint (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER UNIQUE NOT NULL,
finger_code TEXT NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES user(id)
);
-- 开门记录表
CREATE TABLE open_record (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
open_type INTEGER NOT NULL, -- 1人脸 2指纹 3密码
open_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES user(id)
);
-- 系统日志表
CREATE TABLE system_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
log_type INTEGER,
log_content TEXT NOT NULL,
log_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
第 3 天 软键盘|权限开锁|状态机|MFC 抽奖 UI
今日任务
Qt 嵌入式软键盘开发、用户人脸 / 指纹 / 账号绑定、本地数据持久化
人脸 / 指纹 / 密码三种开锁逻辑、GPIO 继电器控制、超时容错、业务状态机完善
今日必背 3 题
- 嵌入式状态机设计的优势?
- 嵌入式设备本地数据持久化实现方式?
- MFC 消息映射机制原理?
- Linux:管道 |、重定向 > >>日志查看 tail
- C++:函数重载、默认参数
当日交付
三模式完整开锁功能、用户注册业务闭环
一、Qt 嵌入式软键盘开发
二、用户人脸 / 指纹 / 账号绑定
三、本地数据持久化
四、人脸 / 指纹 / 密码三种开锁逻辑
五、GPIO 继电器控制
六、超时容错
七、业务状态机完善
八、交付物
三模式完整开锁功能
用户注册业务闭环
----------------------------------晚上补齐,因为断电写的都没了啊啊啊啊啊啊--------------