znlgis 博客

GIS开发与技术分享

第15章:压力与摇杆传感器

本章介绍薄膜压力传感器、摇杆模块的工作原理与应用案例。


15.1 薄膜压力传感器

15.1.1 工作原理

薄膜压力传感器是一种可变电阻式传感器:

无压力: 高阻抗 (>1MΩ)
有压力: 阻值降低 (几KΩ~几十KΩ)

阻值与压力呈反比关系。

15.1.2 电路设计

     VCC
      │
    [R1 10K]  分压电阻
      │
      ├───────── ADC
      │
   [压力传感器]
      │
     GND

15.1.3 检测代码

'''
实验:薄膜压力传感器
接线:S 端连接 GP26
'''
from machine import ADC
import time

pressure = ADC(26)

def read_pressure():
    """读取压力值(0-100%)"""
    raw = pressure.read_u16()
    # 反转:压力越大,电阻越小,电压越低
    inverted = 65535 - raw
    percent = inverted / 65535 * 100
    return raw, percent

while True:
    raw, percent = read_pressure()
    
    bar = '█' * int(percent / 5)
    print(f"ADC: {raw:5d}  压力: {percent:5.1f}% |{bar}")
    
    time.sleep(0.1)

15.1.4 力度检测等级

'''
实验:压力等级判断
'''
def pressure_level(raw):
    """判断压力等级"""
    if raw > 60000:
        return "无压力", 0
    elif raw > 50000:
        return "轻触", 1
    elif raw > 35000:
        return "按压", 2
    elif raw > 20000:
        return "用力", 3
    else:
        return "重压", 4

while True:
    raw = pressure.read_u16()
    level, grade = pressure_level(raw)
    print(f"ADC: {raw}  等级: {level} ({grade})")
    time.sleep(0.2)

15.2 摇杆模块

15.2.1 模块介绍

PS2 摇杆模块包含:

  • X 轴电位器
  • Y 轴电位器
  • Z 轴按键

15.2.2 数据编码

状态 X 轴 ADC Y 轴 ADC
中心 ~32000 ~32000
~0 ~32000
~65535 ~32000
~32000 ~0
~32000 ~65535

按键:按下=高电平,松开=低电平

15.2.3 基础读取

'''
实验:摇杆模块读取
接线:X→GP26, Y→GP27, B→GP22
'''
from machine import ADC, Pin
import time

joystick_x = ADC(26)
joystick_y = ADC(27)
joystick_btn = Pin(22, Pin.IN)

while True:
    x = joystick_x.read_u16()
    y = joystick_y.read_u16()
    btn = joystick_btn.value()
    
    print(f"X: {x:5d}  Y: {y:5d}  按键: {btn}")
    time.sleep(0.1)

15.2.4 方向判断

'''
实验:摇杆方向判断
'''
from machine import ADC, Pin
import time

x_adc = ADC(26)
y_adc = ADC(27)
btn = Pin(22, Pin.IN)

# 中心值和死区
CENTER = 32768
DEADZONE = 10000

def get_direction():
    """获取摇杆方向"""
    x = x_adc.read_u16()
    y = y_adc.read_u16()
    
    dx = x - CENTER
    dy = y - CENTER
    
    # 死区判断
    if abs(dx) < DEADZONE and abs(dy) < DEADZONE:
        return "CENTER", 0, 0
    
    # 判断主方向
    if abs(dx) > abs(dy):
        if dx > 0:
            return "RIGHT", dx, dy
        else:
            return "LEFT", dx, dy
    else:
        if dy > 0:
            return "DOWN", dx, dy
        else:
            return "UP", dx, dy

while True:
    direction, dx, dy = get_direction()
    pressed = "按下" if btn.value() == 1 else ""
    print(f"方向: {direction:6s}  偏移: ({dx:+6d}, {dy:+6d})  {pressed}")
    time.sleep(0.1)

15.2.5 模拟量转角度

'''
实验:摇杆角度计算
'''
import math

def joystick_to_angle():
    """计算摇杆角度和力度"""
    x = x_adc.read_u16() - CENTER
    y = y_adc.read_u16() - CENTER
    
    # 计算角度(弧度转角度)
    angle = math.atan2(y, x) * 180 / math.pi
    
    # 计算力度(0-100%)
    magnitude = min(100, math.sqrt(x*x + y*y) / CENTER * 100)
    
    return angle, magnitude

while True:
    angle, magnitude = joystick_to_angle()
    
    if magnitude > 10:  # 死区
        print(f"角度: {angle:+6.1f}°  力度: {magnitude:5.1f}%")
    else:
        print("摇杆居中")
    
    time.sleep(0.1)

15.3 摇杆控制应用

15.3.1 控制 LED 亮度

'''
实验:摇杆控制 LED 亮度和闪烁
X 轴: 亮度
Y 轴: 闪烁速度
'''
from machine import ADC, Pin, PWM
import time

x_adc = ADC(26)
y_adc = ADC(27)
led = PWM(Pin(0))
led.freq(1000)

while True:
    x = x_adc.read_u16()
    y = y_adc.read_u16()
    
    # X 轴控制亮度
    led.duty_u16(x)
    
    # Y 轴控制闪烁(值越大越快)
    blink_delay = max(0.05, (65535 - y) / 65535)
    
    time.sleep(blink_delay)

15.3.2 摇杆控制舵机

'''
实验:摇杆控制舵机
X 轴: 水平舵机
Y 轴: 垂直舵机
'''
from machine import ADC, Pin, PWM
import time

x_adc = ADC(26)
y_adc = ADC(27)

servo_h = PWM(Pin(16))
servo_v = PWM(Pin(17))
servo_h.freq(50)
servo_v.freq(50)

def set_servo_angle(servo, angle):
    """设置舵机角度 (0-180)"""
    # 脉宽: 0.5ms (0°) ~ 2.5ms (180°)
    # 周期: 20ms (50Hz)
    duty = int((angle / 180 * 2 + 0.5) / 20 * 65535)
    servo.duty_u16(duty)

def joystick_to_angle(adc_value):
    """ADC 值转角度"""
    return adc_value / 65535 * 180

while True:
    x = x_adc.read_u16()
    y = y_adc.read_u16()
    
    angle_h = joystick_to_angle(x)
    angle_v = joystick_to_angle(y)
    
    set_servo_angle(servo_h, angle_h)
    set_servo_angle(servo_v, angle_v)
    
    print(f"水平: {angle_h:.0f}°  垂直: {angle_v:.0f}°")
    time.sleep(0.05)

15.3.3 游戏控制器

'''
实验:摇杆游戏控制器类
'''
from machine import ADC, Pin
import time

class Joystick:
    """摇杆控制器类"""
    
    def __init__(self, x_pin, y_pin, btn_pin, deadzone=0.15):
        self.x_adc = ADC(x_pin)
        self.y_adc = ADC(y_pin)
        self.btn = Pin(btn_pin, Pin.IN)
        self.deadzone = deadzone
        
        # 校准中心值
        self.center_x = 32768
        self.center_y = 32768
    
    def calibrate(self):
        """校准中心值"""
        samples = 50
        x_sum = sum(self.x_adc.read_u16() for _ in range(samples))
        y_sum = sum(self.y_adc.read_u16() for _ in range(samples))
        self.center_x = x_sum // samples
        self.center_y = y_sum // samples
        print(f"校准完成: 中心=({self.center_x}, {self.center_y})")
    
    def read_normalized(self):
        """读取归一化值 (-1.0 ~ 1.0)"""
        x = (self.x_adc.read_u16() - self.center_x) / 32768
        y = (self.y_adc.read_u16() - self.center_y) / 32768
        
        # 应用死区
        if abs(x) < self.deadzone:
            x = 0
        if abs(y) < self.deadzone:
            y = 0
        
        return x, y, self.btn.value()
    
    def get_dpad(self):
        """获取方向键状态"""
        x, y, btn = self.read_normalized()
        
        left = x < -0.5
        right = x > 0.5
        up = y < -0.5
        down = y > 0.5
        
        return {
            'left': left,
            'right': right,
            'up': up,
            'down': down,
            'btn': btn == 1
        }

# 使用
joy = Joystick(26, 27, 22)
joy.calibrate()

while True:
    dpad = joy.get_dpad()
    
    if any([dpad['left'], dpad['right'], dpad['up'], dpad['down']]):
        directions = []
        if dpad['up']: directions.append('↑')
        if dpad['down']: directions.append('↓')
        if dpad['left']: directions.append('←')
        if dpad['right']: directions.append('→')
        print(f"方向: {''.join(directions)}")
    
    if dpad['btn']:
        print("按键!")
    
    time.sleep(0.05)

15.4 旋转编码器

15.4.1 工作原理

旋转编码器通过正交编码检测旋转方向和角度:

顺时针:
A: ──┐  ┌──┐  ┌──
    └──┘  └──┘
B: ─┐  ┌──┐  ┌──┐
   └──┘  └──┘  └─

逆时针:
B: ──┐  ┌──┐  ┌──
    └──┘  └──┘
A: ─┐  ┌──┐  ┌──┐
   └──┘  └──┘  └─

15.4.2 编码器解码

'''
实验:旋转编码器
接线:CLK→GP16, DT→GP17, SW→GP18
'''
from machine import Pin
import time

clk = Pin(16, Pin.IN, Pin.PULL_UP)
dt = Pin(17, Pin.IN, Pin.PULL_UP)
sw = Pin(18, Pin.IN, Pin.PULL_UP)

position = 0
last_clk = clk.value()

def encoder_callback(pin):
    global position, last_clk
    
    current_clk = clk.value()
    
    if current_clk != last_clk:
        if dt.value() != current_clk:
            position += 1
        else:
            position -= 1
        
        print(f"位置: {position}")
    
    last_clk = current_clk

clk.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=encoder_callback)

while True:
    if sw.value() == 0:
        position = 0
        print("位置重置!")
        time.sleep(0.3)
    time.sleep(0.01)

15.5 本章小结

本章介绍了压力与摇杆传感器:

  1. 薄膜压力传感器
    • 阻值随压力变化
    • 适用于力度检测
  2. 摇杆模块
    • 双轴电位器 + 按键
    • 输出两路模拟量 + 一路数字量
  3. 数据编码
    • ADC 范围 0-65535
    • 中心值约 32768
  4. 应用场景
    • 游戏控制
    • 云台控制
    • 交互界面

下一章将学习 PWM 脉宽调制技术。