pos 机 显示驱动移植

时间: 2025-08-06 07:14:23 浏览: 2
<think>嗯,用户这次的问题是关于POS机的显示驱动移植方法和教程,需要结合之前关于POS机显示"bringup"错误的讨论。用户似乎是个嵌入式开发工程师,正在为POS设备移植显示屏驱动,遇到了底层适配问题。从引用材料看,用户提供了四段技术文档:[1]讲pinctrl驱动基础,[2]涉及rfkill驱动和补丁应用,[3]描述硬件引脚管理,[4]展示触摸屏坐标获取。这说明用户可能面临的是显示屏初始化失败问题,而非单纯应用层问题。之前讨论中POS机启动卡在"bringup"界面,现在用户显然是在尝试修复这个问题。考虑从三个层面设计回复:首先是驱动架构适配。需要解释如何将已有的显示驱动(如ST7789V)移植到POS机主控(类似STM32F407),重点在FrameBuffer接口适配。引用[4]的触摸坐标获取API可以补充说明输入设备联动,但根据用户核心需求,优先保证显示输出。然后是引脚配置。引用[1]的pinctrl机制和引用[3]的MCU冲突检测是关键,必须强调设备树里配置GPIO引脚复用功能时,要兼顾显示接口(如RGB/MIPI)和POS机原有外设(如打印机串口)的冲突检测。最后是启动流程优化。之前"bringup"错误可能源于驱动初始化顺序,参考systemd服务依赖管理。考虑建议用systemd-analyzeplot生成时序图,把显示驱动服务放在rfkill之后(引用[2]提到蓝牙使能控制),但要在帧缓冲之前初始化。用户提供的rtk_rfkill驱动示例比较重要,显示驱动在POS环境中可能需要类似的gpio-poweroff控制。在相关问题中增加显示与输入设备同步的问题,引用[4]的触摸获取API可能正是用户下一步需要的。</think> 将驱动程序移植到POS机的显示系统需遵循嵌入式Linux驱动开发流程,核心步骤及方法如下: --- ### **一、驱动移植核心流程** #### 1. **硬件适配分析** - **确认主控平台** 例如基于POS机使用的SoC(如STM32F407[^4]),查阅其技术手册中显示控制器(如LTDC/MIPI-DSI)的寄存器映射。 - **引脚复用配置** 根据硬件原理图配置显示接口的引脚复用,参考`pinctrl`驱动机制[^1]: ```c static struct pinctrl_pin_desc pos_pins[] = { PINCTRL_PIN(0, "LCD_R0"), // 红色数据线0 PINCTRL_PIN(1, "LCD_CLK"), // 像素时钟 ... // 其他显示相关引脚 }; ``` 需确保引脚功能、电气属性(上拉/下拉、驱动强度)匹配显示屏规格。 #### 2. **显示驱动框架移植** - **选择适配框架** - 帧缓冲驱动(FrameBuffer):适合RGB/MIPI接口显示屏 - DRM/KMS驱动:支持现代GPU加速 - **关键代码适配** 以FrameBuffer为例,需实现: ```c static struct fb_ops pos_fb_ops = { .owner = THIS_MODULE, .fb_fillrect = cfb_fillrect, // 矩形填充 .fb_setcolreg = pos_setcolreg, // 颜色寄存器配置 .fb_blank = pos_blank, // 屏幕休眠控制 }; static int pos_probe(struct platform_device *pdev) { struct fb_info *info; info = framebuffer_alloc(sizeof(*par), &pdev->dev); info->fbops = &pos_fb_ops; // 绑定操作函数集 register_framebuffer(info); // 注册驱动 } ``` #### 3. **设备树(Device Tree)配置** 在`.dts`文件中声明硬件资源: ```dts lcd0: display@40016800 { compatible = "pos,lcd-controller"; reg = <0x40016800 0x200>; // 控制器寄存器地址范围 pinctrl-names = "default"; pinctrl-0 = <&lcd_pinctrl>; // 绑定引脚配置组 status = "okay"; }; ``` --- ### **二、常见问题解决** #### 1. **显示初始化失败** - **检查点** - 时钟信号是否使能(`clk_prepare_enable(lcd_clk)`) - 电源时序是否正确(上电顺序需遵循硬件手册) - `pinctrl`配置冲突(用`pinctrl_get`调试引脚状态[^1]) #### 2. **显示内容异常** ```bash # 查看FrameBuffer调试信息 cat /sys/class/graphics/fb0/virtual_size # 分辨率校验 dmesg | grep -i "fb" # 驱动加载日志 ``` --- ### **三、POS机特有注意点** 1. **外设冲突处理** 扫描枪/打印机等外设可能占用相同总线,需在设备树中分配独立资源(参考MCU引脚冲突检测机制[^3])。 2. **实时性保障** 交易界面需低延迟刷新,建议采用双缓冲机制: ```c fb_var_screeninfo.yoffset = new_buffer_offset; // 切换显存地址 fb_pan_display(info, &info->var); // 触发渲染 ``` 3. **安全认证** PCI-PTS认证要求驱动代码具备内存隔离保护,需禁用`DMA`非安全地址访问。 --- ### **四、验证流程** 1. **基础测试** ```bash echo 1 > /sys/class/graphics/fb0/blank # 控制屏幕休眠 cat /dev/urandom > /dev/fb0 # 填充随机像素 ``` 2. **性能测试** 使用`fbtest`工具测量帧率与时延: ```bash fbtest -f 32 -c red -d 60 # 红色60FPS刷屏测试 ``` > **移植案例**:某Ingenico POS机移植ST7789V驱动时,因`pinctrl`未配置开漏输出导致SCL信号异常,添加`PIN_CONFIG_DRIVE_OPEN_DRAIN`属性后解决[^1][^4]。
阅读全文

相关推荐

改进一下代码: #include <iostream> using namespace std; int main() { char p_zero; char p_one; char pos_a_first = '_'; char pos_a_second = '_'; char pos_a_third = '_'; char pos_b_first = '_'; char pos_b_second = '_'; char pos_b_third = '_'; char pos_c_first = '_'; char pos_c_second = '_'; char pos_c_third = '_'; int player_count = 1; cout << "Player 0,请选择棋子x或0:"; cin >> p_zero; if (p_zero == 'x') { p_one = '0'; } else if (p_zero == '0') { p_one = 'x'; } cout << "Player 0先走" << endl; while (1) { cout << " 1 2 3 " << endl; cout << "========================" << endl; cout << " A " << pos_a_first << " | " << pos_a_second << " | " << pos_a_third << " " << endl; cout << " B " << pos_b_first << " | " << pos_b_second << " | " << pos_b_third << " " << endl; cout << " C " << pos_c_first << " | " << pos_c_second << " | " << pos_c_third << " " << endl; cout << "========================" << endl; char pos; if (player_count % 2 == 1) { cout << "请player0选择落子位置(例:A1):"; } else { cout << "请player1选择落子位置(例:A1):"; } cin >> pos; if (player_count % 2 == 1) { if (pos == 'A1') { pos_a_first = p_zero; } else if (pos == 'A2') { pos_a_second = p_zero; } else if (pos == 'A3') { pos_a_third = p_zero; } else if (pos == 'B1') { pos_b_first = p_zero; } else if (pos == 'B2') { pos_b_second = p_zero; } else if (pos == 'B3') { pos_b_third = p_zero; } else if (pos == 'C1') { pos_c_first = p_zero; } else if (pos == 'C2') { pos_c_second = p_zero; } else if (pos == 'C3') { pos_c_third = p_zero; } } else { if (pos == 'A1') { pos_a_first = p_one; } else if (pos == 'A2') { pos_a_second = p_one; } else if (pos == 'A3') { pos_a_third = p_one; } else if (pos == 'B1') { pos_b_first = p_one; } else if (pos == 'B2') { pos_b_second = p_one; } else if (pos == 'B3') { pos_b_third = p_one; } else if (pos == 'C1') { pos_c_first = p_one; } else if (pos == 'C2') { pos_c_second = p_one; } else if (pos == 'C3') { pos_c_third = p_one; } } player_count++; } return 0; }

import sensor, image, time, math from pyb import Pin, Timer # 如果不需要UART通信,可以注释掉这行 from machine import UART ############################################################# # 全局常量定义 - 集中管理所有配置参数 ############################################################# # 调试和性能选项 DEBUG = True # 设置为True可以在串行终端显示信息 DRAW_DETECTION = False # 设置为False可以减少图像处理负担,不在显示窗口显示检测框 SLEEP_MS = 20 # 循环延时(毫秒),增大可降低CPU使用率,但会降低响应速度 USE_MORPHOLOGY = True # 设置为False可以跳过形态学操作,提高性能 RESOLUTION = sensor.QQVGA # 可选: sensor.QQVGA (160x120) 或 sensor.QVGA (320x240) SHOW_STATS = False # 不在屏幕上显示FPS和其他信息 AUTO_THRESHOLD = True # 设置为True启用自适应阈值 THRESHOLD_OFFSET = 10 # 自适应阈值偏移量 MAX_LOST_FRAMES = 30 # 目标丢失多少帧后执行特定操作 # 图像处理参数 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 TEMPLATE_MATCH_THRESHOLD = 0.65 # 模板匹配阈值 TEMPLATE_MATCH_STEP = 8 # 模板匹配步长,增大可提高性能但降低精度 BLOB_AREA_THRESHOLD = 100 # Blob检测面积阈值 BLOB_PIXELS_THRESHOLD = 100 # Blob检测像素阈值 BLOB_MERGE_MARGIN = 5 # Blob合并边距 RECT_THRESHOLD = 1000 # 矩形检测阈值 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 ERROR_SUM_MAX = 500 # 积分项最大值 ERROR_SUM_MIN = -500 # 积分项最小值 PW_TO_ANGLE = 0.09 # 脉冲宽度到角度的转换系数 (180/2000) # 舵机参数 SERVO_MIN_PW = 500 # 最小脉冲宽度 (0度) SERVO_MAX_PW = 2500 # 最大脉冲宽度 (180度) SERVO_FREQ = 50 # 舵机PWM频率 # 搜索模式参数 SEARCH_DWELL_FRAMES = 10 # 每个搜索位置停留的帧数 # 预定义搜索位置 - 使用常量避免重复创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] ############################################################# # 系统初始化函数 ############################################################# def init_camera(): """初始化并优化摄像头设置""" sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) # 使用灰度模式 sensor.set_framesize(RESOLUTION) sensor.skip_frames(time=2000) # 等待设置生效 # 优化自动设置 sensor.set_auto_gain(False) # 关闭自动增益 sensor.set_auto_whitebal(False) # 关闭自动白平衡 sensor.set_auto_exposure(False, exposure_us=10000) # 固定曝光时间 # 优化图像设置 sensor.set_brightness(0) sensor.set_contrast(0) # 保持镜像设置 sensor.set_hmirror(False) sensor.set_vflip(False) def init_servos(): """初始化舵机控制""" # 创建两个不同的定时器实例 tim_pan = Timer(2, freq=SERVO_FREQ) # 使用常量定义PWM频率 tim_tilt = Timer(4, freq=SERVO_FREQ) # 水平舵机(Pan) - 控制左右旋转 pan_servo = tim_pan.channel(1, Timer.PWM, pin=Pin("P6")) # 垂直舵机(Tilt) - 控制上下旋转 tilt_servo = tim_tilt.channel(1, Timer.PWM, pin=Pin("P8")) return pan_servo, tilt_servo def load_template(): """加载模板图像,使用异常处理确保程序不会崩溃""" try: template = image.Image("bazi.pgm") if DEBUG: print("成功加载模板图像 bazi.pgm") return template except Exception as e: if DEBUG: print("加载模板图像失败:", e) return None # 优化的舵机控制函数 def set_servo_angle(ch, angle): """ 设置舵机角度 (0-180度) 使用位运算和预计算常量优化计算 """ # 使用位运算优化限制角度范围 angle = min(180, max(0, angle)) # 使用预计算的常量和整数运算优化脉冲宽度计算 # 0.5ms-2.5ms脉冲范围 (500-2500) # 使用整数运算然后转换为整数,避免浮点运算 pulse = SERVO_MIN_PW + int((angle * (SERVO_MAX_PW - SERVO_MIN_PW)) / 180) # 直接设置脉冲宽度 ch.pulse_width(pulse) ############################################################# # PID控制函数 ############################################################# def apply_pid_control(current_pos, center_pos, error_sum, last_error): """ 应用PID控制算法 优化计算效率,减少内存使用 参数: current_pos: 当前位置 center_pos: 目标位置 error_sum: 累计误差 last_error: 上一次误差 返回: (输出值, 更新后的累计误差, 当前误差) """ # 计算误差(使用减法而不是乘以-1,更高效) # 负号是因为舵机方向与坐标系方向相反 error = center_pos - current_pos # 更新积分项 - 使用就地操作符 error_sum += error # 使用位运算优化限制积分项 # 使用常量避免重复计算 error_sum = min(ERROR_SUM_MAX, max(ERROR_SUM_MIN, error_sum)) # 计算微分项 - 直接使用减法 error_diff = error - last_error # 使用预计算的乘法减少浮点运算 # 可以考虑使用整数运算然后再转换为浮点数 output = (KP * error) + (KI * error_sum) + (KD * error_diff) # 使用元组返回多个值,减少临时变量 return output, error_sum, error ############################################################# # 系统初始化 ############################################################# # 初始化摄像头 init_camera() # 加载模板图像 template = load_template() # 初始化舵机 pan_servo, tilt_servo = init_servos() # 设置初始位置:90度(中间位置) pan_angle = 90 tilt_angle = 90 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) time.sleep(1) # 等待舵机到位 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 # PID控制变量 pan_error_sum = 0 tilt_error_sum = 0 last_pan_error = 0 last_tilt_error = 0 # 二值化阈值 - 调整这个值以适应您的环境光线条件 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 ############################################################# # 辅助函数 ############################################################# def print_config(): """显示当前配置信息""" if DEBUG: print("=== 激光点追踪系统配置 ===") print("分辨率: {}".format("160x120" if RESOLUTION == sensor.QQVGA else "320x240")) print("二值化阈值: {}".format("自适应 (偏移量={})".format(THRESHOLD_OFFSET) if AUTO_THRESHOLD else BINARY_THRESHOLD)) print("使用形态学操作: {}".format(USE_MORPHOLOGY)) print("绘制检测结果: {}".format(DRAW_DETECTION)) print("显示统计信息: {}".format(SHOW_STATS)) print("循环延时: {}ms".format(SLEEP_MS)) print("PID参数: KP={}, KI={}, KD={}".format(KP, KI, KD)) print("目标丢失处理: {}帧后启动搜索".format(MAX_LOST_FRAMES)) print("=========================") def process_image(img, need_stats=False): """ 处理图像:二值化和形态学操作 优化内存使用和计算效率 参数: img: 要处理的图像 need_stats: 是否需要计算图像统计信息 返回: 处理后的图像 """ # 获取统计信息(仅在需要时) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("均值: {:.1f}, 中值: {}, 标准差: {:.1f}".format( stats.mean(), stats.median(), stats.stdev())) # 使用自适应阈值或固定阈值进行二值化 if AUTO_THRESHOLD and need_stats: threshold_value = max(10, min(250, stats.mean() - THRESHOLD_OFFSET)) thresholds = [(0, threshold_value)] else: thresholds = BINARY_THRESHOLD # 二值化处理 img.binary(thresholds) # 可选的形态学操作 if USE_MORPHOLOGY: img.erode(1) img.dilate(1) return img # 主循环 ############################################################# # 主循环 ############################################################# # 创建时钟对象来跟踪FPS clock = time.clock() # 显示初始配置 print_config() # 初始化追踪状态变量 lost_frames_count = 0 # 目标丢失计数器 searching = False # 是否正在执行搜索 search_state = 0 # 搜索模式的状态 search_step_counter = 0 # 搜索步骤的计数器 while True: clock.tick() # 更新FPS计时器 # 直接处理单幅图像,不保留副本 img = sensor.snapshot() # 预先检查是否需要统计信息 need_stats = (DEBUG and SHOW_STATS) or AUTO_THRESHOLD or template # 获取统计信息(在原图上) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("亮度均值: {:.1f}, 标准差: {:.1f}".format(stats.mean(), stats.stdev())) # 仅当需要时才进行二值化 if AUTO_THRESHOLD or BINARY_THRESHOLD: threshold = stats.mean() - THRESHOLD_OFFSET if AUTO_THRESHOLD else BINARY_THRESHOLD[0][1] img.binary([(0, max(10, min(250, threshold)))]) if USE_MORPHOLOGY: img.erode(1) img.dilate(1) # 图像处理结果直接存储在img中 # 不再需要DISPLAY_RAW切换,所有处理都在原图进行 # 优化模板匹配 - 减少计算和内存使用 template_matched = False if template and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 使用快速条件检查跳过不必要的模板匹配 poor_image_quality = (current_stats.mean() > 200 or current_stats.mean() < 20 or (current_stats.max() - current_stats.min()) < 30) if poor_image_quality: if DEBUG: print("图像质量不佳,跳过模板匹配") else: try: # 使用更高效的模板匹配参数 # 增加step值减少计算量,使用更简单的搜索方法 r = img.find_template(template, 0.65, step=8, search=image.SEARCH_DS) if r: # 找到匹配 - 使用元组解包直接获取坐标 x, y, w, h = r # 预计算中心点和图像中心 center_x = x + (w >> 1) # 使用位移代替除法 center_y = y + (h >> 1) img_center_x = img.width() >> 1 img_center_y = img.height() >> 1 # 重置状态变量 - 使用一行代码减少指令数 lost_frames_count = search_state = search_step_counter = 0 searching = False # 标记模板匹配成功 template_matched = True # 使用我们之前定义的PID控制函数 # 计算与图像中心的偏差 dx = center_x - img_center_x dy = center_y - img_center_y # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度并限制范围 pan_angle = max(0, min(180, pan_angle + pan_output)) tilt_angle = max(0, min(180, tilt_angle + tilt_output)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 只在调试模式下打印信息 if DEBUG: print("FPS: {:.1f}, 模板匹配中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) elif DEBUG: print("模板匹配失败") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 if DEBUG: print("模板匹配错误") # 优化阈值计算 - 减少计算和内存使用 threshold_value = 0 if AUTO_THRESHOLD and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 简化阈值计算 threshold_value = max(10, min(250, current_stats.mean() - THRESHOLD_OFFSET)) if DEBUG: print("自适应阈值: {}".format(threshold_value)) else: # 使用固定阈值 threshold_value = BINARY_THRESHOLD[0][1] # 优化形态学操作 - 只在必要时执行 if USE_MORPHOLOGY: # 直接使用open操作,减少内存使用 img.open(1) # 反转图像以便黑色矩形变为白色(如果需要) # binary_img.invert() # 优化检测逻辑 - 减少异常处理和条件判断 rects = [] blobs = [] # 使用标志变量而不是异常处理来控制流程 rect_detection_success = False rects = [] # 尝试矩形检测 try: rects = img.find_rects(threshold=1000) rect_detection_success = len(rects) > 0 if DEBUG and rect_detection_success: print("使用find_rects检测到", len(rects), "个矩形") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 rect_detection_success = False if DEBUG: print("切换到find_blobs作为备选") # 如果矩形检测失败,尝试blob检测 if not rect_detection_success: # 确保统计信息已计算 current_stats = None if need_stats: current_stats = img.get_statistics() # 根据图像亮度调整阈值 if current_stats and current_stats.mean() > 200: threshold_value = max(threshold_value, 220) if DEBUG: print("图像较亮,调整阈值为", threshold_value) # 使用优化的参数进行blob检测 try: blobs = img.find_blobs([(threshold_value, 255)], area_threshold=100, pixels_threshold=100, merge=True, margin=5) if DEBUG and blobs: print("使用find_blobs检测到", len(blobs), "个blob") except Exception: # 简化异常处理 blobs = [] # 直接使用检测结果,不创建副本以节省内存 detected_rects = rects if 'rects' in locals() and rects else [] detected_blobs = blobs if 'blobs' in locals() and blobs else [] if detected_blobs: if DEBUG: print("检测到", len(detected_blobs), "个blob") # 创建一个更高效的自定义矩形类来模拟find_rects的返回对象 class CustomRect: __slots__ = ['x', 'y', 'w', 'h', '_cx', '_cy'] # 使用__slots__减少内存使用,添加缓存属性 def __init__(self, x, y, w, h): self.x = x self.y = y self.w = w self.h = h # 预计算中心点,避免重复计算 self._cx = x + w // 2 self._cy = y + h // 2 def rect(self): # 直接返回元组,避免创建新的元组 return self.x, self.y, self.w, self.h @property def cx(self): # 作为属性访问 return self._cx @property def cy(self): # 作为属性访问 return self._cy # 优化:将检测到的blobs转换为自定义矩形对象,并添加到detected_rects中 # 预先分配足够的空间,避免动态扩展列表 if not detected_rects: detected_rects = [None] * len(detected_blobs) else: # 扩展现有列表以容纳新的矩形 detected_rects.extend([None] * len(detected_blobs)) # 使用索引直接赋值,避免append操作 start_idx = len(detected_rects) - len(detected_blobs) for i, b in enumerate(detected_blobs): # 从blob创建一个自定义矩形对象 x, y, w, h = b.rect() detected_rects[start_idx + i] = CustomRect(x, y, w, h) # 如果模板匹配没有成功,则尝试使用矩形检测 if not template_matched and detected_rects: # 选择最大矩形(假设主要目标) max_rect = max(detected_rects, key=lambda r: r.w * r.h) if detected_rects else None if not max_rect: if DEBUG: print("未检测到有效矩形") continue # 计算矩形中心点 # 兼容不同矩形对象类型 if hasattr(max_rect, 'rect'): x, y, w, h = max_rect.rect() else: x, y, w, h = max_rect.x, max_rect.y, max_rect.w, max_rect.h center_x = x + w // 2 center_y = y + h // 2 # 不再需要标记矩形和中心点 # 重置丢失计数器和搜索状态 lost_frames_count = 0 searching = False search_state = 0 search_step_counter = 0 # 计算与图像中心的偏差 img_center_x = img.width() // 2 img_center_y = img.height() // 2 # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度 pan_angle += pan_output tilt_angle += tilt_output # 限制舵机角度在有效范围内 pan_angle = max(0, min(180, pan_angle)) tilt_angle = max(0, min(180, tilt_angle)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 打印调试信息 if DEBUG: print("FPS: {:.1f}, 矩形中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) # 不在屏幕上显示统计信息 else: # 增加丢失计数器 lost_frames_count += 1 if lost_frames_count > MAX_LOST_FRAMES: # 启动搜索模式 - 只在首次进入时初始化 if not searching: searching = True search_state = 0 search_step_counter = 0 # 优化的搜索模式实现 - 使用常量和位运算优化 # 预定义搜索位置 - 使用常量避免重复创建列表 # 这些位置在全局定义,避免每次循环重新创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] # 使用常量获取当前位置 - 避免每次重新计算 current_pos = SEARCH_POSITIONS[search_state] # 批量设置舵机角度 - 减少通信开销 set_servo_angle(pan_servo, current_pos[0]) set_servo_angle(tilt_servo, current_pos[1]) # 更新计数器和状态 - 使用位运算优化 search_step_counter += 1 if search_step_counter > 10: # 停留10帧 # 使用位运算优化模运算,如果SEARCH_POSITIONS长度是2的幂 # 否则保留原来的模运算 search_state = (search_state + 1) % len(SEARCH_POSITIONS) search_step_counter = 0 # 使用位运算优化角度计算 # 将脉冲宽度转换为角度 (500-2500 对应 0-180度) # 使用预计算的常量减少运算 # 180/2000 = 0.09 PW_TO_ANGLE = 0.09 pan_angle = (pan_servo.pulse_width() - 500) * PW_TO_ANGLE tilt_angle = (tilt_servo.pulse_width() - 500) * PW_TO_ANGLE # 只在调试模式下打印信息 if DEBUG: print("搜索模式: 状态 {}, 步骤 {}".format(search_state, search_step_counter)) if DEBUG: print("未检测到矩形, FPS: {:.1f}, 丢失帧数: {}".format(clock.fps(), lost_frames_count)) # 不在屏幕上显示统计信息 # 控制循环频率,减少CPU使用率 time.sleep_ms(SLEEP_MS) # 可配置的延时,控制更新率 这个代码报错import sensor, image, time, math from pyb import Pin, Timer # 如果不需要UART通信,可以注释掉这行 from machine import UART ############################################################# # 全局常量定义 - 集中管理所有配置参数 ############################################################# # 调试和性能选项 DEBUG = True # 设置为True可以在串行终端显示信息 DRAW_DETECTION = False # 设置为False可以减少图像处理负担,不在显示窗口显示检测框 SLEEP_MS = 20 # 循环延时(毫秒),增大可降低CPU使用率,但会降低响应速度 USE_MORPHOLOGY = True # 设置为False可以跳过形态学操作,提高性能 RESOLUTION = sensor.QQVGA # 可选: sensor.QQVGA (160x120) 或 sensor.QVGA (320x240) SHOW_STATS = False # 不在屏幕上显示FPS和其他信息 AUTO_THRESHOLD = True # 设置为True启用自适应阈值 THRESHOLD_OFFSET = 10 # 自适应阈值偏移量 MAX_LOST_FRAMES = 30 # 目标丢失多少帧后执行特定操作 # 图像处理参数 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 TEMPLATE_MATCH_THRESHOLD = 0.65 # 模板匹配阈值 TEMPLATE_MATCH_STEP = 8 # 模板匹配步长,增大可提高性能但降低精度 BLOB_AREA_THRESHOLD = 100 # Blob检测面积阈值 BLOB_PIXELS_THRESHOLD = 100 # Blob检测像素阈值 BLOB_MERGE_MARGIN = 5 # Blob合并边距 RECT_THRESHOLD = 1000 # 矩形检测阈值 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 ERROR_SUM_MAX = 500 # 积分项最大值 ERROR_SUM_MIN = -500 # 积分项最小值 PW_TO_ANGLE = 0.09 # 脉冲宽度到角度的转换系数 (180/2000) # 舵机参数 SERVO_MIN_PW = 500 # 最小脉冲宽度 (0度) SERVO_MAX_PW = 2500 # 最大脉冲宽度 (180度) SERVO_FREQ = 50 # 舵机PWM频率 # 搜索模式参数 SEARCH_DWELL_FRAMES = 10 # 每个搜索位置停留的帧数 # 预定义搜索位置 - 使用常量避免重复创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] ############################################################# # 系统初始化函数 ############################################################# def init_camera(): """初始化并优化摄像头设置""" sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) # 使用灰度模式 sensor.set_framesize(RESOLUTION) sensor.skip_frames(time=2000) # 等待设置生效 # 优化自动设置 sensor.set_auto_gain(False) # 关闭自动增益 sensor.set_auto_whitebal(False) # 关闭自动白平衡 sensor.set_auto_exposure(False, exposure_us=10000) # 固定曝光时间 # 优化图像设置 sensor.set_brightness(0) sensor.set_contrast(0) # 保持镜像设置 sensor.set_hmirror(False) sensor.set_vflip(False) def init_servos(): """初始化舵机控制""" # 创建两个不同的定时器实例 tim_pan = Timer(2, freq=SERVO_FREQ) # 使用常量定义PWM频率 tim_tilt = Timer(4, freq=SERVO_FREQ) # 水平舵机(Pan) - 控制左右旋转 pan_servo = tim_pan.channel(1, Timer.PWM, pin=Pin("P6")) # 垂直舵机(Tilt) - 控制上下旋转 tilt_servo = tim_tilt.channel(1, Timer.PWM, pin=Pin("P8")) return pan_servo, tilt_servo def load_template(): """加载模板图像,使用异常处理确保程序不会崩溃""" try: template = image.Image("bazi.pgm") if DEBUG: print("成功加载模板图像 bazi.pgm") return template except Exception as e: if DEBUG: print("加载模板图像失败:", e) return None # 优化的舵机控制函数 def set_servo_angle(ch, angle): """ 设置舵机角度 (0-180度) 使用位运算和预计算常量优化计算 """ # 使用位运算优化限制角度范围 angle = min(180, max(0, angle)) # 使用预计算的常量和整数运算优化脉冲宽度计算 # 0.5ms-2.5ms脉冲范围 (500-2500) # 使用整数运算然后转换为整数,避免浮点运算 pulse = SERVO_MIN_PW + int((angle * (SERVO_MAX_PW - SERVO_MIN_PW)) / 180) # 直接设置脉冲宽度 ch.pulse_width(pulse) ############################################################# # PID控制函数 ############################################################# def apply_pid_control(current_pos, center_pos, error_sum, last_error): """ 应用PID控制算法 优化计算效率,减少内存使用 参数: current_pos: 当前位置 center_pos: 目标位置 error_sum: 累计误差 last_error: 上一次误差 返回: (输出值, 更新后的累计误差, 当前误差) """ # 计算误差(使用减法而不是乘以-1,更高效) # 负号是因为舵机方向与坐标系方向相反 error = center_pos - current_pos # 更新积分项 - 使用就地操作符 error_sum += error # 使用位运算优化限制积分项 # 使用常量避免重复计算 error_sum = min(ERROR_SUM_MAX, max(ERROR_SUM_MIN, error_sum)) # 计算微分项 - 直接使用减法 error_diff = error - last_error # 使用预计算的乘法减少浮点运算 # 可以考虑使用整数运算然后再转换为浮点数 output = (KP * error) + (KI * error_sum) + (KD * error_diff) # 使用元组返回多个值,减少临时变量 return output, error_sum, error ############################################################# # 系统初始化 ############################################################# # 初始化摄像头 init_camera() # 加载模板图像 template = load_template() # 初始化舵机 pan_servo, tilt_servo = init_servos() # 设置初始位置:90度(中间位置) pan_angle = 90 tilt_angle = 90 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) time.sleep(1) # 等待舵机到位 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 # PID控制变量 pan_error_sum = 0 tilt_error_sum = 0 last_pan_error = 0 last_tilt_error = 0 # 二值化阈值 - 调整这个值以适应您的环境光线条件 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 ############################################################# # 辅助函数 ############################################################# def print_config(): """显示当前配置信息""" if DEBUG: print("=== 激光点追踪系统配置 ===") print("分辨率: {}".format("160x120" if RESOLUTION == sensor.QQVGA else "320x240")) print("二值化阈值: {}".format("自适应 (偏移量={})".format(THRESHOLD_OFFSET) if AUTO_THRESHOLD else BINARY_THRESHOLD)) print("使用形态学操作: {}".format(USE_MORPHOLOGY)) print("绘制检测结果: {}".format(DRAW_DETECTION)) print("显示统计信息: {}".format(SHOW_STATS)) print("循环延时: {}ms".format(SLEEP_MS)) print("PID参数: KP={}, KI={}, KD={}".format(KP, KI, KD)) print("目标丢失处理: {}帧后启动搜索".format(MAX_LOST_FRAMES)) print("=========================") def process_image(img, need_stats=False): """ 处理图像:二值化和形态学操作 优化内存使用和计算效率 参数: img: 要处理的图像 need_stats: 是否需要计算图像统计信息 返回: 处理后的图像 """ # 获取统计信息(仅在需要时) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("均值: {:.1f}, 中值: {}, 标准差: {:.1f}".format( stats.mean(), stats.median(), stats.stdev())) # 使用自适应阈值或固定阈值进行二值化 if AUTO_THRESHOLD and need_stats: threshold_value = max(10, min(250, stats.mean() - THRESHOLD_OFFSET)) thresholds = [(0, threshold_value)] else: thresholds = BINARY_THRESHOLD # 二值化处理 img.binary(thresholds) # 可选的形态学操作 if USE_MORPHOLOGY: img.erode(1) img.dilate(1) return img # 主循环 ############################################################# # 主循环 ############################################################# # 创建时钟对象来跟踪FPS clock = time.clock() # 显示初始配置 print_config() # 初始化追踪状态变量 lost_frames_count = 0 # 目标丢失计数器 searching = False # 是否正在执行搜索 search_state = 0 # 搜索模式的状态 search_step_counter = 0 # 搜索步骤的计数器 while True: clock.tick() # 更新FPS计时器 # 直接处理单幅图像,不保留副本 img = sensor.snapshot() # 预先检查是否需要统计信息 need_stats = (DEBUG and SHOW_STATS) or AUTO_THRESHOLD or template # 获取统计信息(在原图上) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("亮度均值: {:.1f}, 标准差: {:.1f}".format(stats.mean(), stats.stdev())) # 仅当需要时才进行二值化 if AUTO_THRESHOLD or BINARY_THRESHOLD: threshold = stats.mean() - THRESHOLD_OFFSET if AUTO_THRESHOLD else BINARY_THRESHOLD[0][1] img.binary([(0, max(10, min(250, threshold)))]) if USE_MORPHOLOGY: img.erode(1) img.dilate(1) # 图像处理结果直接存储在img中 # 不再需要DISPLAY_RAW切换,所有处理都在原图进行 # 优化模板匹配 - 减少计算和内存使用 template_matched = False if template and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 使用快速条件检查跳过不必要的模板匹配 poor_image_quality = (current_stats.mean() > 200 or current_stats.mean() < 20 or (current_stats.max() - current_stats.min()) < 30) if poor_image_quality: if DEBUG: print("图像质量不佳,跳过模板匹配") else: try: # 使用更高效的模板匹配参数 # 增加step值减少计算量,使用更简单的搜索方法 r = img.find_template(template, 0.65, step=8, search=image.SEARCH_DS) if r: # 找到匹配 - 使用元组解包直接获取坐标 x, y, w, h = r # 预计算中心点和图像中心 center_x = x + (w >> 1) # 使用位移代替除法 center_y = y + (h >> 1) img_center_x = img.width() >> 1 img_center_y = img.height() >> 1 # 重置状态变量 - 使用一行代码减少指令数 lost_frames_count = search_state = search_step_counter = 0 searching = False # 标记模板匹配成功 template_matched = True # 使用我们之前定义的PID控制函数 # 计算与图像中心的偏差 dx = center_x - img_center_x dy = center_y - img_center_y # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度并限制范围 pan_angle = max(0, min(180, pan_angle + pan_output)) tilt_angle = max(0, min(180, tilt_angle + tilt_output)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 只在调试模式下打印信息 if DEBUG: print("FPS: {:.1f}, 模板匹配中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) elif DEBUG: print("模板匹配失败") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 if DEBUG: print("模板匹配错误") # 优化阈值计算 - 减少计算和内存使用 threshold_value = 0 if AUTO_THRESHOLD and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 简化阈值计算 threshold_value = max(10, min(250, current_stats.mean() - THRESHOLD_OFFSET)) if DEBUG: print("自适应阈值: {}".format(threshold_value)) else: # 使用固定阈值 threshold_value = BINARY_THRESHOLD[0][1] # 优化形态学操作 - 只在必要时执行 if USE_MORPHOLOGY: # 直接使用open操作,减少内存使用 img.open(1) # 反转图像以便黑色矩形变为白色(如果需要) # binary_img.invert() # 优化检测逻辑 - 减少异常处理和条件判断 rects = [] blobs = [] # 使用标志变量而不是异常处理来控制流程 rect_detection_success = False rects = [] # 尝试矩形检测 try: rects = img.find_rects(threshold=1000) rect_detection_success = len(rects) > 0 if DEBUG and rect_detection_success: print("使用find_rects检测到", len(rects), "个矩形") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 rect_detection_success = False if DEBUG: print("切换到find_blobs作为备选") # 如果矩形检测失败,尝试blob检测 if not rect_detection_success: # 确保统计信息已计算 current_stats = None if need_stats: current_stats = img.get_statistics() # 根据图像亮度调整阈值 if current_stats and current_stats.mean() > 200: threshold_value = max(threshold_value, 220) if DEBUG: print("图像较亮,调整阈值为", threshold_value) # 使用优化的参数进行blob检测 try: blobs = img.find_blobs([(threshold_value, 255)], area_threshold=100, pixels_threshold=100, merge=True, margin=5) if DEBUG and blobs: print("使用find_blobs检测到", len(blobs), "个blob") except Exception: # 简化异常处理 blobs = [] # 直接使用检测结果,不创建副本以节省内存 detected_rects = rects if 'rects' in locals() and rects else [] detected_blobs = blobs if 'blobs' in locals() and blobs else [] if detected_blobs: if DEBUG: print("检测到", len(detected_blobs), "个blob") # 创建一个更高效的自定义矩形类来模拟find_rects的返回对象 class CustomRect: __slots__ = ['x', 'y', 'w', 'h', '_cx', '_cy'] # 使用__slots__减少内存使用,添加缓存属性 def __init__(self, x, y, w, h): self.x = x self.y = y self.w = w self.h = h # 预计算中心点,避免重复计算 self._cx = x + w // 2 self._cy = y + h // 2 def rect(self): # 直接返回元组,避免创建新的元组 return self.x, self.y, self.w, self.h @property def cx(self): # 作为属性访问 return self._cx @property def cy(self): # 作为属性访问 return self._cy # 优化:将检测到的blobs转换为自定义矩形对象,并添加到detected_rects中 # 预先分配足够的空间,避免动态扩展列表 if not detected_rects: detected_rects = [None] * len(detected_blobs) else: # 扩展现有列表以容纳新的矩形 detected_rects.extend([None] * len(detected_blobs)) # 使用索引直接赋值,避免append操作 start_idx = len(detected_rects) - len(detected_blobs) for i, b in enumerate(detected_blobs): # 从blob创建一个自定义矩形对象 x, y, w, h = b.rect() detected_rects[start_idx + i] = CustomRect(x, y, w, h) # 如果模板匹配没有成功,则尝试使用矩形检测 if not template_matched and detected_rects: # 选择最大矩形(假设主要目标) max_rect = max(detected_rects, key=lambda r: r.w * r.h) if detected_rects else None if not max_rect: if DEBUG: print("未检测到有效矩形") continue # 计算矩形中心点 # 兼容不同矩形对象类型 if hasattr(max_rect, 'rect'): x, y, w, h = max_rect.rect() else: x, y, w, h = max_rect.x, max_rect.y, max_rect.w, max_rect.h center_x = x + w // 2 center_y = y + h // 2 # 不再需要标记矩形和中心点 # 重置丢失计数器和搜索状态 lost_frames_count = 0 searching = False search_state = 0 search_step_counter = 0 # 计算与图像中心的偏差 img_center_x = img.width() // 2 img_center_y = img.height() // 2 # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度 pan_angle += pan_output tilt_angle += tilt_output # 限制舵机角度在有效范围内 pan_angle = max(0, min(180, pan_angle)) tilt_angle = max(0, min(180, tilt_angle)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 打印调试信息 if DEBUG: print("FPS: {:.1f}, 矩形中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) # 不在屏幕上显示统计信息 else: # 增加丢失计数器 lost_frames_count += 1 if lost_frames_count > MAX_LOST_FRAMES: # 启动搜索模式 - 只在首次进入时初始化 if not searching: searching = True search_state = 0 search_step_counter = 0 # 优化的搜索模式实现 - 使用常量和位运算优化 # 预定义搜索位置 - 使用常量避免重复创建列表 # 这些位置在全局定义,避免每次循环重新创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] # 使用常量获取当前位置 - 避免每次重新计算 current_pos = SEARCH_POSITIONS[search_state] # 批量设置舵机角度 - 减少通信开销 set_servo_angle(pan_servo, current_pos[0]) set_servo_angle(tilt_servo, current_pos[1]) # 更新计数器和状态 - 使用位运算优化 search_step_counter += 1 if search_step_counter > 10: # 停留10帧 # 使用位运算优化模运算,如果SEARCH_POSITIONS长度是2的幂 # 否则保留原来的模运算 search_state = (search_state + 1) % len(SEARCH_POSITIONS) search_step_counter = 0 # 使用位运算优化角度计算 # 将脉冲宽度转换为角度 (500-2500 对应 0-180度) # 使用预计算的常量减少运算 # 180/2000 = 0.09 PW_TO_ANGLE = 0.09 pan_angle = (pan_servo.pulse_width() - 500) * PW_TO_ANGLE tilt_angle = (tilt_servo.pulse_width() - 500) * PW_TO_ANGLE # 只在调试模式下打印信息 if DEBUG: print("搜索模式: 状态 {}, 步骤 {}".format(search_state, search_step_counter)) if DEBUG: print("未检测到矩形, FPS: {:.1f}, 丢失帧数: {}".format(clock.fps(), lost_frames_count)) # 不在屏幕上显示统计信息 # 控制循环频率,减少CPU使用率 time.sleep_ms(SLEEP_MS) # 可配置的延时,控制更新率

#include "oled.h" #include "stdlib.h" #include "oledfont.h" #include "delay.h" //OLED的显存 //存放格式如下. //[0]0 1 2 3 ... 127 //[1]0 1 2 3 ... 127 //[2]0 1 2 3 ... 127 //[3]0 1 2 3 ... 127 //[4]0 1 2 3 ... 127 //[5]0 1 2 3 ... 127 //[6]0 1 2 3 ... 127 //[7]0 1 2 3 ... 127 /********************************************** //IIC Start **********************************************/ /********************************************** //IIC Start **********************************************/ void IIC_Start() { OLED_SCLK_Set() ; OLED_SDIN_Set(); OLED_SDIN_Clr(); OLED_SCLK_Clr(); } /********************************************** //IIC Stop **********************************************/ void IIC_Stop() { OLED_SCLK_Set() ; // OLED_SCLK_Clr(); OLED_SDIN_Clr(); OLED_SDIN_Set(); } void IIC_Wait_Ack() { //GPIOB->CRH &= 0XFFF0FFFF; //设置PB12为上拉输入模式 //GPIOB->CRH |= 0x00080000; // OLED_SDA = 1; // delay_us(1); //OLED_SCL = 1; //delay_us(50000); /* while(1) { if(!OLED_SDA) //判断是否接收到OLED 应答信号 { //GPIOB->CRH &= 0XFFF0FFFF; //设置PB12为通用推免输出模式 //GPIOB->CRH |= 0x00030000; return; } } */ OLED_SCLK_Set() ; OLED_SCLK_Clr(); } /********************************************** // IIC Write byte **********************************************/ void Write_IIC_Byte(unsigned char IIC_Byte) { unsigned char i; unsigned char m,da; da=IIC_Byte; OLED_SCLK_Clr(); for(i=0;i<8;i++) { m=da; // OLED_SCLK_Clr(); m=m&0x80; if(m==0x80) {OLED_SDIN_Set();} else OLED_SDIN_Clr(); da=da<<1; OLED_SCLK_Set(); OLED_SCLK_Clr(); } } /********************************************** // IIC Write Command **********************************************/ void Write_IIC_Command(unsigned char IIC_Command) { IIC_Start(); Write_IIC_Byte(0x78); //Slave address,SA0=0 IIC_Wait_Ack(); Write_IIC_Byte(0x00); //write command IIC_Wait_Ack(); Write_IIC_Byte(IIC_Command); IIC_Wait_Ack(); IIC_Stop(); } /********************************************** // IIC Write Data **********************************************/ void Write_IIC_Data(unsigned char IIC_Data) { IIC_Start(); Write_IIC_Byte(0x78); //D/C#=0; R/W#=0 IIC_Wait_Ack(); Write_IIC_Byte(0x40); //write data IIC_Wait_Ack(); Write_IIC_Byte(IIC_Data); IIC_Wait_Ack(); IIC_Stop(); } void OLED_WR_Byte(unsigned dat,unsigned cmd) { if(cmd) { Write_IIC_Data(dat); } else { Write_IIC_Command(dat); } } /******************************************** // fill_Picture ********************************************/ void fill_picture(unsigned char fill_Data) { unsigned char m,n; for(m=0;m<8;m++) { OLED_WR_Byte(0xb0+m,0); //page0-page1 OLED_WR_Byte(0x00,0); //low column start address OLED_WR_Byte(0x10,0); //high column start address for(n=0;n<128;n++) { OLED_WR_Byte(fill_Data,1); } } } /***********************Delay****************************************/ void Delay_50ms(unsigned int Del_50ms) { unsigned int m; for(;Del_50ms>0;Del_50ms--) for(m=6245;m>0;m--); } void Delay_1ms(unsigned int Del_1ms) { unsigned char j; while(Del_1ms--) { for(j=0;j<123;j++); } } //坐标设置 void OLED_Set_Pos(unsigned char x, unsigned char y) { OLED_WR_Byte(0xb0+y,OLED_CMD); OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD); OLED_WR_Byte((x&0x0f),OLED_CMD); } //开启OLED显示 void OLED_Display_On(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON } //关闭OLED显示 void OLED_Display_Off(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF } //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! void OLED_Clear(void) { u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); } //更新显示 } void OLED_On(void) { u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA); } //更新显示 } //在指定位置显示一个字符,包括部分字符 //x:0~127 //y:0~63 //mode:0,反白显示;1,正常显示 //size:选择字体 16/12 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size) { unsigned char c=0,i=0; c=chr-' ';//得到偏移后的值 if(x>Max_Column-1){x=0;y=y+2;} if(Char_Size ==16) { OLED_Set_Pos(x,y); for(i=0;i<8;i++) OLED_WR_Byte(F8X16[c*16+i],OLED_DATA); OLED_Set_Pos(x,y+1); for(i=0;i<8;i++) OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA); } else { OLED_Set_Pos(x,y); for(i=0;i<6;i++) OLED_WR_Byte(F6x8[c][i],OLED_DATA); } } //m^n函数 u32 oled_pow(u8 m,u8 n) { u32 result=1; while(n--)result*=m; return result; } //显示2个数字 //x,y :起点坐标 //len :数字的位数 //size:字体大小 //mode:模式 0,填充模式;1,叠加模式 //num:数值(0~4294967295); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2) { u8 t,temp; u8 enshow=0; for(t=0;t<len;t++) { temp=(num/oled_pow(10,len-t-1))%10; if(enshow==0&&t<(len-1)) { if(temp==0) { OLED_ShowChar(x+(size2/2)*t,y,' ',size2); continue; }else enshow=1; } OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); } } //显示一个字符号串 void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size) { unsigned char j=0; while (chr[j]!='\0') { OLED_ShowChar(x,y,chr[j],Char_Size); x+=8; if(x>120){x=0;y+=2;} j++; } } //显示汉字 void OLED_ShowCHinese(u8 x,u8 y,u8 no) { u8 t,adder=0; OLED_Set_Pos(x,y); for(t=0;t<16;t++) { OLED_WR_Byte(Hzk[2*no][t],OLED_DATA); adder+=1; } OLED_Set_Pos(x,y+1); for(t=0;t<16;t++) { OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA); adder+=1; } } /***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/ void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]) { unsigned int j=0; unsigned char x,y; if(y1%8==0) y=y1/8; else y=y1/8+1; for(y=y0;y<y1;y++) { OLED_Set_Pos(x0,y); for(x=x0;x<x1;x++) { OLED_WR_Byte(BMP[j++],OLED_DATA); } } } //初始化SSD1306 void OLED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能A端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB,10,11 GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11); DelayMs(800); OLED_WR_Byte(0xAE,OLED_CMD);//--display off OLED_WR_Byte(0x00,OLED_CMD);//---set low column address OLED_WR_Byte(0x10,OLED_CMD);//---set high column address OLED_WR_Byte(0x40,OLED_CMD);//--set start line address OLED_WR_Byte(0xB0,OLED_CMD);//--set page address OLED_WR_Byte(0x81,OLED_CMD); // contract control OLED_WR_Byte(0xFF,OLED_CMD);//--128 OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64) OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset OLED_WR_Byte(0x00,OLED_CMD);// OLED_WR_Byte(0xD5,OLED_CMD);//set osc division OLED_WR_Byte(0x80,OLED_CMD);// OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off OLED_WR_Byte(0x05,OLED_CMD);// OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period OLED_WR_Byte(0xF1,OLED_CMD);// OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion OLED_WR_Byte(0x12,OLED_CMD);// OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh OLED_WR_Byte(0x30,OLED_CMD);// OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable OLED_WR_Byte(0x14,OLED_CMD);// OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel OLED_Clear(); } 改写为上个要求

大家在看

recommend-type

SMPTE ST-2082技术标准

SMPTE ST 2082-1:速率为 11.88Gb/s 和 11.88/1.001Gb/s 的 12G-SDI(包括多链路 12G-SDI),提供由任何 ST 2082-x 映射映射的数据.
recommend-type

matlab飞行轨迹代码-msa-toolkit:这是在MATLAB中开发的用于模拟火箭6自由度动力学的代码

matlab飞行模拟代码msa-工具包 MSA 工具包是存储任务分析团队实施的代码的存储库。 它由几个文件夹组成,将在下面的段落中简要介绍。 模拟器 这是在MATLAB中开发的用于模拟6自由度火箭动力学的代码。 该模拟器可预测 3D 轨迹、远地点、作用在火箭上的力以及各种其他空气动力学数据。 数据 包含当前飞行数据、火箭几何形状和模拟参数的文件夹。 通用功能 在该文件夹中,存储了工具包代码中使用的常用函数。 autoMatricesProtub 此代码允许使用 Missile DATCOM 自动计算火箭空气动力学系数,适用于不同的气闸配置。 空气动力学优化 此代码实现了火箭的空气动力学优化。 优化变量是鳍弦和高度、鳍形状、卵形长度和卵形形状。 代码使用遗传算法达到目的。 远地点分析 当结构质量已知且具有一定程度的不确定性时,此代码使用不同的电机执行主要的远地点分析,以选择最好的电机。 敏感性分析 该代码实现了对火箭上升阶段的敏感性分析。 有两种类型的分析可用:确定性和随机性。 在确定性分析中,可以改变空气动力学系数的标称值和火箭的结构质量。 变化的相对幅度由用户设置,并且对于分析中考虑
recommend-type

5g核心网和关键技术和功能介绍-nokia.rar

5g核心网和关键技术和功能介绍-nokia.rar
recommend-type

MSG和TNT应变梯度塑性单元-ABAQUS非线性用户单元的开发

MSG和TNT应变梯度塑性单元 引入高阶应变-应变的梯度 需要形函数对坐标的二阶导数 在经典的单元中只有形函数对坐标的一阶导数 非局部加权积分
recommend-type

STM32 的DMAMUX使用说明.pdf

DMAMUX其实就是DMA控制器前一级的多路选择器,有了这个选择器就不用再像F1,F4系列那样每个通道(数据流)要固定选择指定的外设,有了多路选择器就可以任意选择,外设使用DMA方式时无需再选择指定的DMA通道(数据流),任意通道(数据流)都可以

最新推荐

recommend-type

集中采购管理实施细则表单.doc

集中采购管理实施细则表单.doc
recommend-type

解决无法获取网络图片问题,提供PNG素材下载

根据提供的文件信息,我们可以确定知识点主要集中在网络图片获取、素材下载以及特定格式PNG图片的使用和命名规则上。 首先,我们来探讨“无法获取网络图片”这一问题。在互联网环境中,获取网络图片的过程通常涉及几个关键技术点:HTTP/HTTPS协议、网络请求处理、图片资源的定位与下载、以及浏览器或者应用程序对图片的缓存和处理。在这一过程中可能会遇到的问题有网络连接问题、目标服务器配置错误、资源访问权限受限、图片资源不存在或已被移除、跨域访问限制(CORS)、以及客户端代码错误等。 对于“素材下载 PNG素材 网页素材”,我们需要了解PNG图片的特性以及素材下载的相关技术。PNG(Portable Network Graphics)是一种无损数据压缩的位图图形格式,它支持索引、灰度、RGB三种颜色模式以及alpha通道透明度。PNG格式广泛用于网络图片下载,因为它提供了优秀的压缩性能且没有版权限制。在网页设计中,PNG图片因其高保真的特性,可以作为网页背景、图标和按钮的素材。素材下载通常是设计师或者开发人员通过搜索引擎、专门的素材网站或者内容分发网络(CDN)来获取所需的图片、音频、视频等资源。 紧接着,“无法获取网络图片”这一标签指向了一个普遍的技术问题,即客户端在尝试从互联网上下载图片资源时遭遇的失败。这可能发生在使用Web浏览器、桌面应用程序、移动应用或者任何其它形式的客户端软件上。问题的原因可能包括客户端网络设置问题、防火墙限制、代理服务器配置、DNS解析错误等。 最后,观察压缩包子文件的文件名称列表,我们可以看到三个不同的文件名:“无法获取网络@3x.png”、“无法获取网络@2x.png”以及“无法获取网络.png”。这些名称暗示了这些图片可能被用作网页开发中的Retina显示技术,其中“@2x”和“@3x”分别指代在普通分辨率显示器和高分辨率显示器(比如Retina显示屏)上使用的图片资源。数字“2”和“3”通常表示图片的DPI(每英寸点数)倍数,意味着在屏幕上以更高的像素密度显示,以达到更好的视觉效果。 综合上述分析,可以总结以下知识点: 1. 网络图片获取的过程及其可能遇到的问题和技术要点。 2. PNG图片格式的技术特性和其在网页素材中的应用。 3. 网页素材下载的过程、途径和可能出现的问题。 4. Retina显示技术及其在图片资源命名中的应用。 5. 通过文件名称识别图片用途和显示需求。 以上就是从给定文件信息中提取的知识点,希望对解决网络图片获取、素材下载和素材管理等问题提供帮助。
recommend-type

【西门子PLC固件升级全攻略】:20分钟快速掌握固件更新流程

# 摘要 随着工业自动化的发展,可编程逻辑控制器(PLC)作为核心部件,其固件升级成为了提高系统性能、确保安全性和引入新功能的重要手段。本文详细介绍了PLC固件升级的整个过程,从准备工作、理论知识、实战操作到升级后的验证与维护,全面阐述了固件升级的关键步骤和注意事项。通过分析固件升级对系统性能的影响、检查硬件兼容性、准备合适的更新环境,本文旨在为工程师提供一套系统的固件升级指
recommend-type

Transformer做短期时序销量预测可行吗

<think>我们正在讨论Transformer模型在短期时间序列销量预测中的应用及效果评估。根据用户的问题,我们需要分析Transformer模型在短期销量预测中的可行性和效果。参考引用中提到了ARIMA模型和迭代方法(如DeepAR、DSSM、ConvTrans等),这些可以作为对比的基准。首先,Transformer模型最初是为自然语言处理设计的,但其自注意力机制能够捕捉序列中的长期依赖关系,因此也被应用于时间序列预测。在短期预测中,虽然传统方法(如ARIMA)可能因为简单而高效,但Transformer在处理非线性关系和多个相关时间序列方面可能更有优势。效果评估方面,我们可以参考引用[
recommend-type

华为SVN连接失败解决方案及SVNDrv驱动更新指南

标题中提到的是解决华为SVN连接不上问题的SVNDrv驱动文件压缩包,这里面涉及的知识点主要包括华为的SVN工具SecoClient、网络适配器配置、以及驱动文件的操作。下面将详细解释这些知识点: 1. SVN工具SecoClient: SecoClient是华为开发的一个客户端软件,用于连接和管理SVN服务器,SVN(Subversion)是一个开源的版本控制系统,广泛用于计算机软件的版本管理和代码控制。SecoClient作为客户端,一般需要安装在用户的电脑上,用来提交、更新、查看和管理源代码。 2. Win10上面连接不上的问题及返回码超时: 用户在使用SecoClient时遇到的连接不上问题,提示“接受返回码超时”,这通常是指客户端尝试与SVN服务器进行通信时,在设定的时间内没有得到有效的响应。返回码超时问题可能由多种原因导致,例如网络连接不稳定、防火墙设置、SVN服务器响应慢、或者是客户端与服务器之间的配置不正确。 3. 网络适配器配置: 网络适配器是电脑硬件中负责数据通信的部分。在本问题中,具体的操作为禁用网络适配器中的“SVN Adapter V1.0”,这一操作可能会影响到SecoClient的网络连接,特别是如果SVN Adapter是一个虚拟的网络适配器或者专门用于SecoClient连接的适配器时。 4. 驱动文件SVNDrv.sys的处理: 驱动文件(SVNDrv.sys)是操作系统用来控制硬件和软件资源的一个软件程序,对于SVN工具来说,这个驱动文件可能是用来协助SecoClient与网络适配器进行通信的。如果在连接SVN时遇到问题,解决方案中提到的删除旧的驱动文件并复制新的文件进去,可能是为了修复驱动文件损坏或更新驱动程序。 具体操作步骤为: - 打开“设备管理器”,找到网络适配器部分。 - 在列表中找到“SVN Adapter V1.0”,右键选择“禁用”。 - 导航到系统盘符下的“C:\Windows\System32\drivers”目录。 - 在该目录中找到并删除“SVNDrv.sys”文件。 - 将新下载的“SVNDrv.sys”文件复制到该目录下。 - 最后回到设备管理器,右键点击“SVN Adapter V1.0”,选择“启用”。 5. 标签中的知识点: - SecoClient:华为提供的用于连接和管理SVN服务器的客户端工具。 - SVNAdapter:可能指的是SecoClient在电脑上配置的一个虚拟网络适配器,用于连接到SVN服务器。 - 返回码超时:连接过程中出现的错误提示,表明在预定时间未能完成操作。 【压缩包子文件的文件名称列表】中的“解决华为SVN连接不上问题SVNDrv驱动文件.zip”则表明该压缩包内包含的是用于解决上述问题的相关文件,即新的SVNDrv.sys驱动文件。 综上所述,本知识点的详细解释不仅涵盖了与华为SecoClient工具相关的操作和配置,还包括了网络适配器和驱动文件的基本理解和处理方法。对于遇到类似问题的IT专业人士或普通用户来说,了解这些操作可以有效地解决SVN连接问题,确保软件开发和版本控制工作的顺利进行。
recommend-type

【西门子PLC新手必备手册】:一文掌握硬件安装与配置技巧

# 摘要 本文旨在全面介绍西门子PLC(可编程逻辑控制器)的相关知识与应用,包括硬件安装、配置实践、基础编程技巧、高级功能应用及系统维护与升级。首先,概述了PLC的基本概念与硬件组件,并详细讲解了硬件安装的理论与实践技巧。接着,深
recommend-type

idea 反编译 jar

<think>我们正在讨论如何使用IntelliJIDEA反编译JAR文件。根据用户的问题,我们需要提供清晰的步骤。注意:IntelliJIDEA内置了Java反编译器,可以方便地查看JAR文件中的类文件的源代码。步骤:1.在IntelliJIDEA中,确保已经创建了一个项目(可以是任意项目,甚至是一个空项目)。2.将JAR文件作为库添加到项目中,或者直接打开JAR文件。有两种主要方法:方法一:通过项目工具窗口添加JAR-打开项目后,在项目工具窗口中右键单击要添加JAR的模块或项目根目录。-选择“打开模块设置”(或按F4),或者在较新版本中,选择“项目结构”。-在“项目设置”下选择“库”,然后
recommend-type

思科Z1路由器刷机教程:LEDE固件升级指南

标题 "LEDE-Meraki-Z1.tar.rar" 中涉及的关键词包括 "LEDE" 和 "Meraki-Z1"。LEDE 项目是 OpenWrt 的一个分支,主要针对嵌入式设备的固件开发,提供高度可定制化的固件系统。"Meraki-Z1" 则指向了 Cisco Meraki 的一种特定硬件设备,即 Z1 系列路由器。Cisco Meraki 是思科公司的一个分支,专注于云管理网络解决方案。Z1 路由器被设计用于远程管理和监控,适合中小型企业使用。 描述中提到的“思科 z1 刷机固件 lede”,意指将 LEDE 固件刷入思科的 Z1 系列路由器中,这通常是为了替换原厂固件,实现对设备的更高控制度和扩展更多功能。这一步骤可能需要一定的技术知识,包括对网络设备固件更新的理解和路由器的物理接入权限。 标签“思科z1”进一步确认了设备的身份,即针对的是思科公司生产的 Z1 系列路由器。这一标签对识别适用固件和后续的问题排查提供帮助。 文件压缩包中的文件名称列表提供了两个关键的文件信息: 1. "led-ar71xx-nand-z1-initramfs-kernel.bin":这个文件很可能是固件中使用的初始 RAM 文件系统(initramfs)和内核镜像。在系统启动时,initramfs 负责挂载真正的根文件系统,并且内核在此阶段负责硬件检测和初始化。由于这个文件名中包含“ar71xx”,我们可以推断这个固件可能是为使用 AR71xx 芯片组的设备所设计的。 2. "led-ar71xx-nand-z1-squashfs-sysupgrade.tar":这个文件可能是实际包含 LEDE 固件的系统升级文件。SquashFS 是一种压缩的只读文件系统,适合用于嵌入式设备的固件,因为它可以减少对存储空间的需求。sysupgrade 是 OpenWrt 及其衍生项目如 LEDE 用来升级固件的机制,允许用户在不完全擦除存储的情况下进行固件的升级。 综合以上信息,可以得出以下几点重要知识点: - LEDE 项目是一个专注于嵌入式设备固件的项目,致力于提供可定制化、开源的固件。 - 思科的 Meraki-Z1 路由器系列适合中小企业使用,并提供云管理解决方案。 - “刷机”指的是将新的固件写入到设备中,这一步骤通常需要一定的技术背景。 - LEDE 固件被设计为可以运行在使用 AR71xx 芯片组的网络设备上。 - 固件升级通常通过 initramfs(初始RAM文件系统)和内核来完成硬件初始化和系统引导,而 SquashFS 和 sysupgrade 用于提供高效、便捷的固件升级机制。 在尝试刷入 LEDE 固件之前,用户需要确保他们已经清楚了解所涉及风险,包括但不限于失去保修、设备损坏或可能的数据丢失。同时,确保下载到的固件与设备硬件完全兼容,并且从可靠来源获取固件。刷入新固件前也建议备份当前的配置,以备不时之需。
recommend-type

【Coze工作流入门】:学习制作爆炸盒视频的基本步骤

# 1. Coze工作流基础与视频制作概述 ## 1.1 视频制作的基本概念 视频制作是一个创造性的过程,涉及一系列的步骤,从创意的萌发到最终内容的发布。它不仅仅是技术操作的集合,更是一个故事叙述的艺术形式。在这个过程中,Coze工作流提供了一种系统化的方法来优化和指导视频项目的成功完成。 ## 1.2 Coze工作流的简介 Coze工作流是专为满足现代视频制作需求而设计
recommend-type

sigmod与softmax

<think>首先,用户的问题是:“我想比较sigmoid与softmax函数在机器学习中的区别和应用场景 请问sigmoid函数与softmax函数的区别以及在分类任务中的应用”。我需要基于系统指令和用户可见层的要求来回答。 系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 参考站内引用:引用[1]和引用[2]是关于sigmoid和s