树莓派项目中带条件控制的多线程ADC与霍尔效应检测实现问题
解决方案:带条件控制的多线程ADC采集与霍尔检测
核心问题分析
你的代码缺少线程间通信机制来触发ADC停止,同时未实现采集-绘图的循环逻辑,主要问题包括:
- 未初始化控制线程的信号对象,导致
adcreceiver中self.event.is_set()调用报错 halleffect_monitor未在达到检测次数后触发ADC停止信号,也未调用绘图和循环重置- 主程序未实现重复采集的循环逻辑,资源释放不完整
修正后的完整代码
import spidev import time import numpy as np import matplotlib.pyplot as plt import RPi.GPIO as GPIO import threading class halleffectandadc: def __init__(self, desired_clock_cycles, pin, clock_speed, buff_size): self.spi = spidev.SpiDev() self.spi.open(0, 0) self.spi.max_speed_hz = clock_speed self.buff_size = buff_size self.desired_clock_cycles = desired_clock_cycles GPIO.setmode(GPIO.BCM) self.pin = pin GPIO.setup(pin, GPIO.IN) # 初始化线程控制事件,用于通知ADC线程停止 self.stop_event = threading.Event() # 初始化数据缓冲区 self.reset_buffer() def reset_buffer(self): """重置数据缓冲区和计时,为下一轮采集做准备""" self.buff = np.ndarray((self.buff_size,), dtype="uint16") self.time_start = None self.time_end = None # 清空停止事件 self.stop_event.clear() def get_adc_val(self): response = self.spi.xfer([0x00, 0x00]) return (response[0] << 8) + response[1] def adcreceiver(self): self.time_start = time.perf_counter() i = 0 # 循环条件:缓冲区未填满 且 未收到停止信号 while i < self.buff_size and not self.stop_event.is_set(): self.buff[i] = self.get_adc_val() i += 1 self.time_end = time.perf_counter() if self.stop_event.is_set(): print("ADC线程已停止,准备处理数据") def halleffect_monitor(self): hall_effect_clock_count = 0 while hall_effect_clock_count < self.desired_clock_cycles: if GPIO.wait_for_edge(self.pin, GPIO.RISING, timeout=1000): hall_effect_clock_count += 1 print(f"检测到磁场,累计次数: {hall_effect_clock_count}") # 达到指定次数,触发ADC停止信号 self.stop_event.set() def plot_data(self): if self.time_start is None or self.time_end is None: print("无有效采集数据,跳过绘图") return elapsed_time = self.time_end - self.time_start time_points = np.linspace(0, elapsed_time, len(self.buff), endpoint=True) plt.plot(time_points, self.buff / 65535 * 5) plt.xlabel("时间 (s)", fontsize=12) plt.title("电位器电压变化", fontsize=12) plt.ylabel("电压 (V)", fontsize=12) plt.show() print( f"采集时长: {elapsed_time:.3f}s, 标准差: {np.std(self.buff):.2f}, 最小值: {min(self.buff)}, 最大值: {max(self.buff)}, 平均值: {np.mean(self.buff):.2f}" ) def cleanup(self): """释放硬件资源""" self.spi.close() GPIO.cleanup() if __name__ == "__main__": adc_device = halleffectandadc(desired_clock_cycles=2, pin=17, clock_speed=22000000, buff_size=100000) try: while True: print("=== 开始新一轮采集 ===") adc_device.reset_buffer() # 创建并启动线程 hall_thread = threading.Thread(target=adc_device.halleffect_monitor, daemon=True) adc_thread = threading.Thread(target=adc_device.adcreceiver, daemon=True) hall_thread.start() adc_thread.start() # 等待霍尔线程完成检测 hall_thread.join() # 等待ADC线程处理完剩余采集或停止 adc_thread.join() # 绘制数据 adc_device.plot_data() print("=== 本轮采集完成,等待下一轮 ===") except KeyboardInterrupt: print("\n用户终止程序,正在清理资源...") adc_device.cleanup()
关键改进点说明
线程控制机制:
- 使用
threading.Event()作为线程间信号,霍尔线程在达到检测次数后调用stop_event.set(),ADC线程在循环中实时检查stop_event.is_set(),及时停止采集 - 线程设置为
daemon=True,确保主程序退出时所有线程自动终止
- 使用
循环采集逻辑:
- 添加
reset_buffer()方法重置缓冲区和事件状态,支持重复采集 - 主循环中每次创建新线程,确保状态独立
- 添加
资源管理:
- 添加
cleanup()方法统一释放SPI和GPIO资源 - 主程序捕获
KeyboardInterrupt,确保异常退出时资源被正确清理
- 添加
鲁棒性提升:
- 给
GPIO.wait_for_edge()添加超时,避免线程永久阻塞 - 绘图前检查计时是否有效,防止空数据报错
- 给
内容的提问来源于stack exchange,提问作者shaun0367
相关产品推荐
相关产品推荐

