文章目录
PocketPCR简介
- PocketPCR 是由 GaudiLabs 发起的一个开源项目,旨在提供一种 袖珍型、低成本、USB供电的 PCR 热循环仪(Thermal Cycler)。它允许生物实验者在桌面或野外环境中进行基础的 DNA 扩增实验。
| 链接地址 | 用途说明 |
|---|---|
| http://gaudi.ch/PocketPCR/ | 官方介绍页面(GaudiLabs 官网) |
| https://github.com/GaudiLabs/PocketPCR | GitHub 项目主页,包含源代码、电路图、说明文档等 |
| Essential Biochem Lab Equipment: PCR Machine , How To Assemble PocketPCR ,详细的使用教程(包括加矿物油防止蒸发) | PocketPCR 使用演示视频(YouTube) |

PocketPCR 结构展示




硬件部分
加热头部分

PCB
-
官方代码在硬件部分提供了每个板子的GERBER的zip文件,直接上传到PCB平台就能进行打板。



-
使用的主控为:SAMD21G18B-A (changed from SAML21),通过arduino进行编程:

BOM
- 核心元器件与主芯片
| 设计ator | 数量 | 名称 | 封装 | 订货号 |
|---|---|---|---|---|
| U2 | 1 | SAMD21G18B-A(改自 SAML21) | TQFP-48 7×7mm, 0.5mm 间距 | 556-ATSAMD21G18A-AUT |
| U1 | 1 | TLV1117-33 稳压器 | SOT-223 | 595-TLV1117LV33DCYR |
| Y1 | 1 | 晶振 32.765 kHz | SMD_3215-2pin_3.2x1.5mm | 520-ECS-327-12.5-34B / 449-LFXTAL009678REEL |
| NTC | 1 | 热敏电阻 10K 0.5% 0402 | 0402 | 810-NTCG103JX103DTDS |
- 显示、连接与接口
| 设计ator | 数量 | 名称 | 封装/说明 | 订货号 |
|---|---|---|---|---|
| S1 | 1 | OLED 显示连接器 | - | 649-SFV30R-1STE1HLF |
| S1 | 1 | OLED 显示屏 | OLED_096_1_Flip | - |
| J3 | 1 | USB Type-C 插座 | Amphenol_12401610E4-2A | 523-12401610E4#2A |
| J1 | 1 | 2 针接头 | 1x02 PinHeader | - |
| J2 | 0 | 调试接头(未装) | 2x5, SMD, 1.27mm | - |
- 有源/无源元件
| 设计ator | 数量 | 名称 | 封装 | 订货号 |
|---|---|---|---|---|
| Q1, Q2, Q3 | 3 | N-MOS 管(GSD) | SOT-23 | 863-NTR3C21NZT1G |
| LED1, LED2 | 2 | 指示灯 | 0805 | - |
- 电容、电阻
| 设计ator | 数量 | 值 | 封装 | 订货号 |
|---|---|---|---|---|
| C3 | 1 | 100uF, 6.3V | 1206 | JMK316ABJ107ML-T |
| C2, C5 | 2 | 10uF, 6.3V | 1206 | JMK107ABJ106MA-T |
| C1, C4, C12, C14, C16 | 5 | 1uF | 0805 | 810-C2012X7R1C105M-3 / 603-CC805KKX7R7BB105 |
| C6–C9 | 4 | 100nF | 0805 | - |
| C10, C11 | 2 | 22pF | 0805 | - |
| C15, C17 | 2 | 2.2uF | 0805 | 603-CC805KKX7R7BB225 |
| R1, R9, R10 | 3 | 4.7kΩ | 0805 | - |
| R2, R3 | 2 | 390Ω | 0805 | - |
| R5, R7 | 2 | 390kΩ | 0805 | - |
| R4, R6 | 2 | 4.7kΩ, 0.1% 精度 | - | 279-CPF0805B4K7E1 |
- 散热、风扇、机械件
| 名称 | 数量 | 说明 / 参数 | 订货号 |
|---|---|---|---|
| Blower 风扇 | 1 | 30×30×10mm, 5V, 2Pin | 3010 fan |
| Heat Block | 1 | Ø19.5mm, 铝合金 6082 | - |
| Spring 弹簧 | 1 | 线径 0.4mm / 长度 5mm 或 0.3mm / 10mm | - |
| Feets 脚垫 | 4 | 硅胶脚塞,适配 6.5mm+ | MAKSEY Silicone Caps |
| Screw M3x6mm | 2 | 黑色十字盘头螺钉 DIN 7985 | - |
| Screw M3x16mm | 4 | 黑色十字盘头螺钉 DIN 7985 | - |
| Plastic Screw | 2 | M3 白色尼龙或黑色手拧螺钉 | - |
| X1, X2 | 2+2 | 六角金属柱,10mm + 20mm, M3 | VT DI 10MM / VT DI 20MM |
- 旋转编码器相关
| 名称 | 数量 | 封装 | 订货号 | |
|---|---|---|---|---|
| SW1 | 1 | 带按压开关的旋转编码器 | Alps EC11E | 858-EN11-HSB1AQ15 |
| Encoder Cap | 1 | 旋钮帽,6mm 轴用 | 套件/批量购买 | |
| Rotary Encoder Spring | 1 | 与编码器弹力回弹配合 | - |
- PCB 列表
| 名称 | 数量 | 材质 / 特性 |
|---|---|---|
| PCB1 | 1 | TopHeater 铝基板,导热率 2W,黑色,1.6mm |
| PCB2 | 1 | 主控 PCB,FR4 黑色,1.6mm |
| PCB3 | 1 | 盖子 PCB,FR4 白色,1.2mm |
- 其他物品
| 名称 | 数量 | 描述 | 型号 |
|---|---|---|---|
| Beutel ESD 袋 | 1 | ESD 屏蔽袋 76×127 mm,可重复封口 | ESD BEUTEL-S 127 |
CODE
- 这段
loop()是整个 PocketPCR 热循环控制程序的核心,负责读取传感器、处理界面交互、控制温度状态转换、执行 PID 控制逻辑,并呈现不同的用户界面(主界面、设置界面、运行状态界面等)。整个状态流严格控制 PCR 各个阶段:变性、退火、延伸,并且支持用户自定义每个阶段的时间和温度。
void loop() {
// 关闭加热与风扇,初始化状态
setHeater(0, 0);
// 读取NTC热敏电阻的ADC值
sensorValue = analogRead(analogInPin);
// 应用当前温度与控制值设置加热/冷却输出
setHeater(temperature_mean, TEMPcontrol);
// 将ADC值转换为电压
sensorVoltage = 3.3 * sensorValue / 4096;
// 使用分压公式计算热敏电阻阻值
sensorResistance = (sensorVoltage * NTC_R0) / (3.3 - sensorVoltage);
// 根据 B 参数模型计算温度值(摄氏)
temperature = 1 / (log(sensorResistance * 1000 / NTC_RN) / NTC_B + 1 / NTC_TN) - 273.15;
// 平滑滤波温度值(加权滑动平均)
temperature_mean = (temperature_mean * 3 + temperature) / 4;
// 根据当前 UI 状态 caseUX 进行功能分支
switch (caseUX) {
// 主界面:用户在“Run PCR”与“Setup”之间选择
case CASE_Main:
if (counter > 1) counter = 0;
if (counter < 0) counter = 1;
MenuItem = counter;
draw_main_display(); // 显示主界面
// 用户按键触发
if (!digitalRead(butPin)) {
while (!digitalRead(butPin)); // 等待释放
if (MenuItem == 0) {
caseUX = CASE_Run; // 进入运行界面
casePCR = PCR_set; // 初始化PCR状态
PCRstate = 0;
counter = 0;
}
if (MenuItem == 1) {
caseUX = CASE_Settings; // 进入设置界面
counter = 0;
}
}
break;
// 设置界面:显示与编辑温度/时间设置
case CASE_Settings:
if (counter > 12) counter = 0;
if (counter < 0) counter = 12;
MenuItem = counter;
// 判断当前设置项是否为“分钟”单位
minuteMode = (MenuItem % 2 == 1 && settings.value[MenuItem] > 90);
// 进入具体某一项的编辑界面
if (!digitalRead(butPin)) {
while (!digitalRead(butPin));
caseUX = CASE_EditSettings;
counter = minuteMode ? settings.value[MenuItem] / 60 : settings.value[MenuItem];
}
draw_setup_display(); // 显示设置界面
break;
// 编辑具体某项设置(温度或时间)
case CASE_EditSettings:
if (MenuItem % 2 == 0) { // 温度项
if (MenuItem == 10) {
if (counter < 1) counter = 1;
if (counter > 99) counter = 99;
} else {
if (counter < 25) counter = 25;
if (counter > 99) counter = 99;
}
} else { // 时间项
if (minuteMode) {
if (counter < 2) { minuteMode = false; counter = 90; }
} else {
if (counter > 90) { minuteMode = true; counter = 2; }
if (counter < 0) counter = 0;
}
}
// “保存设置”或“恢复默认”
if (MenuItem == 11) {
caseUX = CASE_Main;
my_flash_store.write(settings); // 保存
}
if (MenuItem == 12) {
caseUX = CASE_Main;
settings = my_flash_store.read(); // 恢复
}
// 保存当前值到 EEPROM 缓存结构
if (MenuItem < 11) {
settings.value[MenuItem] = minuteMode ? counter * 60 : counter;
}
// 返回设置页面
if (!digitalRead(butPin)) {
while (!digitalRead(butPin));
caseUX = CASE_Settings;
counter = MenuItem;
}
draw_setup_display(); // 显示设置项
break;
// PCR 运行状态机
case CASE_Run:
// 温差与积分项更新(用于 PID 控制)
TEMPdif = TEMPset - temperature_mean;
TEMPi += TEMPdif;
// 每200ms 计算一次微分项(TEMPd)
if (millis() - TEMPclick > 200) {
TEMPclick = millis();
TEMPd = TEMPdif_a[4] - TEMPdif;
TEMPdif_a[4] = TEMPdif_a[3];
TEMPdif_a[3] = TEMPdif_a[2];
TEMPdif_a[2] = TEMPdif_a[1];
TEMPdif_a[1] = TEMPdif_a[0];
TEMPdif_a[0] = TEMPdif;
}
// PCR 流程控制状态机
switch (casePCR) {
case PCR_set:
// 设置当前阶段目标温度和时间
TEMPset = settings.value[PCRstate * 2];
TIMEcontrol = settings.value[PCRstate * 2 + 1];
PIDIntegration = false;
casePCR = PCR_transition; // 进入加热阶段
break;
case PCR_transition:
runPID(); // PID控制
draw_run_display();
// 达到目标温度后进入计时状态
if (abs(TEMPset - temperature_mean) < temperature_tollerance) {
PIDIntegration = true;
TEMPi = 0;
TIMEclick = millis();
casePCR = PCR_time;
}
break;
case PCR_time:
runPID(); // PID控制
// 倒计时
TIMEcontrol = settings.value[PCRstate * 2 + 1] - (millis() - TIMEclick) / 1000;
draw_run_display();
// 阶段结束后判断下一个状态
if (TIMEcontrol <= 0) {
if (PCRstate == 4) caseUX = CASE_Done; // Final结束
else if (PCRstate == 3) { // Extension -> Denature (下一轮)
PCRstate = 1;
PCRcycle++;
if (PCRcycle > settings.value[10]) PCRstate = 4; // 到达循环上限,进入 Final
} else {
PCRstate++; // 正常流程进入下一阶段
}
casePCR = PCR_set; // 开始下一阶段
}
break;
default:
break;
}
break;
// PCR 完成界面
case CASE_Done:
TEMPcontrol = 0;
setHeater(temperature_mean, TEMPcontrol); // 停止加热
display.clearDisplay();
display.setFont(&FreeSans9pt7b);
display.fillRect(10, 10, 100, 40, 1);
display.setCursor(15, 30);
display.setTextColor(0);
display.println("PCR Done"); // 显示完成信息
display.display();
display.setFont();
if (!digitalRead(butPin)) {
while (!digitalRead(butPin));
caseUX = CASE_Main;
counter = MenuItem;
}
break;
default:
break;
} // end switch
}
温度控制原理
- 系统通过 热敏电阻 + PID 算法 + 加热器 + 风扇 实现高精度闭环温控。
控制流程概述
-
温度测量:
- 通过热敏电阻(NTC)采集当前温度(使用
analogRead())。 - 使用热敏电阻的阻值换算公式与 B 参数模型将电压转换为温度。
- 通过热敏电阻(NTC)采集当前温度(使用
-
设定目标温度:
- 根据当前的 PCR 状态(如变性、退火、延伸等),读取 EEPROM 里保存的目标温度
TEMPset。
- 根据当前的 PCR 状态(如变性、退火、延伸等),读取 EEPROM 里保存的目标温度
-
误差计算与 PID 控制:
- 计算当前温度与目标温度之间的差值
TEMPdif。 - 调用
runPID()函数,根据 PID 算法(比例P + 微分D + 积分I)计算出控制量TEMPcontrol。
- 计算当前温度与目标温度之间的差值
-
执行加热或冷却:
-
调用
setHeater(),根据TEMPcontrol的正负:- 正值表示加热:启用加热器(加热电阻),关闭风扇。
- 负值表示冷却:启用风扇,关闭加热器。
-
PID 控制 :runPID()
TEMPcontrol = PIDp * TEMPdif + PIDd * TEMPd + (int)PIDIntegration * PIDi * TEMPi;
PIDp,PIDi,PIDd是比例/积分/微分系数。TEMPdif:当前目标温度与实际温度差。TEMPi:积分项(误差累加)。TEMPd:微分项(温差变化速率)。TEMPcontrol是一个浮点值,代表“应该加热多少”或“应该冷却多少”。
风扇控制:setHeater(float temperature, float power)
- 风扇配合加热器,实现了主动降温机制,使温控反应速度更快、更加精准,尤其适合 PCR 中快速温变需求(例如从 95°C 降至 55°C)。
if (TEMPcontrol > 0)
{
// 加热控制(风扇停)
heatPower = power_heating(temperature, power);
analogWrite(fanPin, 0);
analogWrite(heaterPin, heatPower);
}
else
{
// 冷却控制(关闭加热,风扇开启)
heatPower = power_cooling(temperature, power);
analogWrite(fanPin, heatPower);
analogWrite(heaterPin, 0);
}
- 根据
TEMPcontrol的正负,决定启用加热器或风扇。 - 调用
power_heating()/power_cooling()计算合适的 PWM 占空比。
| 情况 | 风扇状态 | 作用说明 |
|---|---|---|
需要升温(TEMPcontrol > 0) | 关闭风扇 | 防止风扇带走热量,集中加热 |
需要降温(TEMPcontrol < 0) | 开启风扇 | 加快冷却速率,迅速降温 |
| 温控关闭(如 PCR 结束) | 风扇关闭 | 节能,系统静止 |


被折叠的 条评论
为什么被折叠?



