Linux驱动程序(PWM接口)与超声波测距

一、利用阿里云服务器实现树莓派外网访问(SSH 反向代理)

1. 树莓派端配置

步骤 1:安装 SSH 服务(若未安装)

sudo apt-get install openssh-server

步骤 2:创建反向代理连接

-p 22:指定阿里云服务器的 SSH 端口

-qngfN:静默模式、后台运行、不分配伪终端

-R:反向代理参数

2222:阿里云服务器用于映射的端口(需自行定义未被占用的端口)

username:阿里云服务器登录用户名

步骤 3:设置开机自启动
将反向代理命令添加到~/.bashrc或创建启动脚本:

echo "ssh -p 22 -qngfNTR 2222:localhost:22 [email protected]" >> ~/.bashrc
2. 阿里云服务器端配置

步骤 1:检查端口监听状态

ss -ntl | grep 2222

若输出类似LISTEN 0 128 :::2222 :::*,说明端口已成功绑定。

步骤 2:配置防火墙(若使用安全组)
在阿里云控制台的安全组规则中添加入方向规则,允许 TCP 协议访问 2222 端口。

3. 外网访问树莓派
# 格式: ssh -p [服务器映射端口] [树莓派用户名]@[服务器IP]
ssh -p 2222 [email protected]

二、树莓派 PWM LED 呼吸灯(含多线程实现)

1. 单线程顺序呼吸灯
import RPi.GPIO as GPIO
import time

# 设置GPIO模式为BOARD编号
GPIO.setmode(GPIO.BOARD)

# 定义LED引脚(可修改为实际连接的引脚)
YELLOW_LED = 11  # 黄灯
GREEN_LED = 16   # 绿灯

# 初始化引脚
GPIO.setup(YELLOW_LED, GPIO.OUT)
GPIO.setup(GREEN_LED, GPIO.OUT)

# 创建PWM对象(频率60Hz)
pwm_yellow = GPIO.PWM(YELLOW_LED, 60)
pwm_green = GPIO.PWM(GREEN_LED, 60)

# 启动PWM(初始占空比0)
pwm_yellow.start(0)
pwm_green.start(0)

try:
    # 循环3次呼吸效果
    for _ in range(3):
        # 黄灯呼吸
        for dc in range(0, 101, 5):
            pwm_yellow.ChangeDutyCycle(dc)
            time.sleep(0.05)
        for dc in range(100, -1, -5):
            pwm_yellow.ChangeDutyCycle(dc)
            time.sleep(0.05)
        
        # 绿灯呼吸
        for dc in range(0, 101, 5):
            pwm_green.ChangeDutyCycle(dc)
            time.sleep(0.05)
        for dc in range(100, -1, -5):
            pwm_green.ChangeDutyCycle(dc)
            time.sleep(0.05)

finally:
    # 停止PWM并清理资源
    pwm_yellow.stop()
    pwm_green.stop()
    GPIO.cleanup()
2. 多线程同时呼吸灯实现
import RPi.GPIO as GPIO
import time
import threading

# 设置GPIO模式
GPIO.setmode(GPIO.BOARD)

# 定义LED引脚
YELLOW_LED = 11
GREEN_LED = 16

# 初始化引脚
GPIO.setup(YELLOW_LED, GPIO.OUT)
GPIO.setup(GREEN_LED, GPIO.OUT)

# 创建PWM对象
pwm_yellow = GPIO.PWM(YELLOW_LED, 60)
pwm_green = GPIO.PWM(GREEN_LED, 60)

# 启动PWM
pwm_yellow.start(0)
pwm_green.start(0)

def yellow_breath():
    """黄灯呼吸函数"""
    for _ in range(3):
        for dc in range(0, 101, 5):
            pwm_yellow.ChangeDutyCycle(dc)
            time.sleep(0.05)
        for dc in range(100, -1, -5):
            pwm_yellow.ChangeDutyCycle(dc)
            time.sleep(0.05)

def green_breath():
    """绿灯呼吸函数"""
    for _ in range(3):
        for dc in range(0, 101, 5):
            pwm_green.ChangeDutyCycle(dc)
            time.sleep(0.05)
        for dc in range(100, -1, -5):
            pwm_green.ChangeDutyCycle(dc)
            time.sleep(0.05)

try:
    # 创建并启动线程
    t1 = threading.Thread(target=yellow_breath)
    t2 = threading.Thread(target=green_breath)
    t1.start()
    t2.start()
    
    # 等待线程结束
    t1.join()
    t2.join()

finally:
    # 清理资源
    pwm_yellow.stop()
    pwm_green.stop()
    GPIO.cleanup()

三、树莓派超声波测距程序

1. 硬件连接

超声波模块 Trig 引脚 → 树莓派 GPIO23(BCM 编号)

超声波模块 Echo 引脚 → 树莓派 GPIO24(BCM 编号)

模块 VCC → 树莓派 5V

模块 GND → 树莓派 GND

2. 完整代码
import RPi.GPIO as GPIO
import time

# 设置GPIO模式为BCM
GPIO.setmode(GPIO.BCM)

# 定义引脚
TRIG_PIN = 23
ECHO_PIN = 24

# 初始化引脚
GPIO.setup(TRIG_PIN, GPIO.OUT)
GPIO.setup(ECHO_PIN, GPIO.IN)

def measure_distance():
    """测量距离函数"""
    # 发送触发信号
    GPIO.output(TRIG_PIN, True)
    time.sleep(0.00001)  # 10微秒
    GPIO.output(TRIG_PIN, False)
    
    # 记录发送和接收时间
    start_time = time.time()
    stop_time = time.time()
    
    # 等待Echo变为高电平(发送超声波)
    while GPIO.input(ECHO_PIN) == 0:
        start_time = time.time()
    
    # 等待Echo变为低电平(接收超声波)
    while GPIO.input(ECHO_PIN) == 1:
        stop_time = time.time()
    
    # 计算距离(声速343m/s,转换为cm)
    time_elapsed = stop_time - start_time
    distance = (time_elapsed * 34300) / 2
    return distance

try:
    print("超声波测距程序启动,按Ctrl+C停止")
    while True:
        dist = measure_distance()
        print(f"距离: {dist:.2f} cm")
        time.sleep(1)  # 每秒测量一次

except KeyboardInterrupt:
    print("程序停止")
finally:
    GPIO.cleanup()

四、Linux 驱动程序控制 PWM(含编译与测试)

1. 准备编译环境
# 安装内核头文件(树莓派专用)
sudo apt-get install raspberrypi-kernel-headers
2. 驱动程序代码

pwmled.h 文件

#define PWMLED_MAX_BRIGHTNESS 1000

typedef enum {
    PWMLED_CMD_SET_BRIGHTNESS = 0x1,
    PWMLED_CMD_GET_BRIGHTNESS,
} pwmled_cmd_t;

pwmled.c 文件

#include 
#include 
#include 
#include 
#include "pwmled.h"

MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("PWM LED Driver");

#define PWM_PERIOD_NS 1000000  // 1ms周期(1000000纳秒)

static struct {
    struct pwm_device *pwm;
    unsigned int brightness;
} pwmled;

// IOCTL处理函数
long pwmled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
    switch (cmd) {
        case PWMLED_CMD_SET_BRIGHTNESS:
            // 限制亮度范围0~1000
            pwmled.brightness = arg > PWMLED_MAX_BRIGHTNESS ? 
                               PWMLED_MAX_BRIGHTNESS : arg;
            // 计算占空比(亮度*1000纳秒)
            int duty_ns = pwmled.brightness * 1000;
            // 配置PWM
            pwm_config(pwmled.pwm, duty_ns, PWM_PERIOD_NS);
            // 启用或禁用PWM
            if (pwmled.brightness > 0) {
                pwm_enable(pwmled.pwm);
            } else {
                pwm_disable(pwmled.pwm);
            }
            break;
        
        case PWMLED_CMD_GET_BRIGHTNESS:
            return pwmled.brightness;
        
        default:
            return -EINVAL;
    }
    return 0;
}

// 文件操作结构体
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = pwmled_ioctl,
};

// 杂项设备结构体
static struct miscdevice pwmled_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "pwmled",
    .fops = &fops,
};

// 模块初始化函数
int __init pwmled_init(void) {
    int ret;
    // 请求PWM通道0(根据实际硬件修改通道号)
    pwmled.pwm = pwm_request(0, "pwmled");
    if (IS_ERR(pwmled.pwm)) {
        ret = PTR_ERR(pwmled.pwm);
        printk(KERN_ERR "Failed to request PWM channel\n");
        return ret;
    }
    
    // 初始化亮度为0
    pwmled.brightness = 0;
    
    // 注册杂项设备
    ret = misc_register(&pwmled_misc);
    if (ret < 0) {
        printk(KERN_ERR "Failed to register misc device\n");
        pwm_free(pwmled.pwm);
        return ret;
    }
    
    printk(KERN_INFO "PWM LED driver initialized\n");
    return 0;
}

// 模块退出函数
void __exit pwmled_exit(void) {
    // 禁用PWM
    pwm_disable(pwmled.pwm);
    // 释放PWM资源
    pwm_free(pwmled.pwm);
    // 注销杂项设备
    misc_deregister(&pwmled_misc);
    printk(KERN_INFO "PWM LED driver unloaded\n");
}

module_init(pwmled_init);
module_exit(pwmled_exit);
3. 编译驱动(Makefile)
obj-m += pwmled.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
4. 加载驱动与测试

步骤 1:编译并加载驱动

# 编译驱动
make
# 加载驱动
sudo insmod pwmled.ko

步骤 2:查看设备节点

ls /dev/pwmled

步骤 3:编写测试程序

import os
import fcntl

# 定义IOCTL命令
PWMLED_CMD_SET_BRIGHTNESS = 0x1
PWMLED_CMD_GET_BRIGHTNESS = 0x2

# 打开设备文件
fd = os.open("/dev/pwmled", os.O_RDWR)
if fd < 0:
    print("Failed to open /dev/pwmled")
    exit(1)

try:
    # 设置亮度(0~1000)
    brightness = 500
    print(f"设置亮度为: {brightness}")
    fcntl.ioctl(fd, PWMLED_CMD_SET_BRIGHTNESS, brightness)
    
    # 等待5秒
    time.sleep(5)
    
    # 获取当前亮度
    current_brightness = fcntl.ioctl(fd, PWMLED_CMD_GET_BRIGHTNESS)
    print(f"当前亮度: {current_brightness}")

finally:
    # 关闭设备文件
    os.close(fd)

步骤 4:卸载驱动

sudo rmmod pwmled

你可能感兴趣的:(linux)