基于嵌入式物联网技术的智慧病房方案设计

news2024/11/26 7:30:50

文章目录

  • 前言
    • 1、要求
    • 2、系统设计
    • 3、功能模块
    • 3、系统功能模块图
  • 一、stm32控制模块原理图
  • 二、各功能模块的实现
    • 1、整个系统的基本配置
    • 2、RTOS多任务
      • 1、设计线程
      • 2、配置主函数代码
    • 3、温湿度读取模块(I2C)
    • 4、LED定时开关灯(pwm)
    • 5、按键实现报警信号
    • 6、脉搏&血氧数据读取
    • 7、UART 转 485 接口与 modbus 通信
  • 三、总结


前言

1、要求

设计一个基于物联网技术的智慧病房管理系统。假设医院住院部的一层病房(走廊两边病房平行分布),病房数量最多60间,每间病房3个床位,编号从 1~180 号。每间病房可采用的设备如下:STM32F103 开发板 1 块,房间温湿度采集模块 1 套( I2C 接口,AHT20 模块),房间自动灯光开关控制器(以 PWM 方式控制,每天早上 7 点渐亮,晚上 22 点渐灭),病人脉搏 & 血氧检测仪 3 套( UART 接口输出脉搏 + 血氧的数字值),床头紧急呼叫按键开关 3 个(按下呼叫)。
每间病房的 STM32F103 开发板通过 UART 转 485 接口,以 mobus 组网方式,连接到护士监控室的 PC 电脑上(上位机)。PC 电脑上可接收每间病房的温湿度数据(周期为 5 分钟)、床头紧急呼叫信号、病人脉搏血氧数据(正常状态下 30 分钟一次采集;当脉搏超过 120 或血氧值低于 90 时切换到危重状态下,实时采集),显示在屏幕上并且保存到 MySQL 数据库里。

2、系统设计

对于该方案,最初的想法是设置一个rtos多任务框架,然后把要求里的框架一个一个都放到线程当中进行完成,包括有AHT的温湿度读取、脉搏&血氧数据读取、按键紧急呼叫、定时开关灯等任务。

3、功能模块

1、脉搏&血氧的数据的检测:脉搏超过120或血氧低于90时,实时采集,否则30分钟采集一次。
2、按键实现紧急呼叫:一个GPIO接口对输入信号的高低电平进行判断。
3、AHT20模块读取温湿度信息:5分钟采集一次。
4、PWM控制灯光定时亮灭:早上7点渐亮,晚上10点渐暗。
5、通过UART转485实现modbus通信。


3、系统功能模块图

在这里插入图片描述

一、stm32控制模块原理图

STM32最小原理图:

在这里插入图片描述
AHT20原理图:

在这里插入图片描述
电路原理图:
在这里插入图片描述

二、各功能模块的实现

1、整个系统的基本配置

  • RCC设置:

在这里插入图片描述

  • SYS设置

在这里插入图片描述

  • UART设置串口模式

在这里插入图片描述
在这里插入图片描述

  • I2C设置DMA模式:

在这里插入图片描述
在这里插入图片描述

  • TIME3:

在这里插入图片描述
TIME2:
在这里插入图片描述
NVIC:
在这里插入图片描述
GPIO:
在这里插入图片描述
时钟树设置:
在这里插入图片描述
移植RT-thread Nano:
在这里插入图片描述

  • 设置时间:
    在这里插入图片描述

2、RTOS多任务

具体移植过程参考:
移植RT-thread Nano完成一个 modbus接口的温湿度Slave设备,让上位机PC通过modbus协议获取温湿度

1、设计线程

  • 在Application/USER文件夹下新建app_rt_thread.c文件
    1、引入所需头文件:
#include "rtthread.h"
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "AHT20.h"

这里我们设置4个子进程,主进程为串口向上位机发送数据,子进程有温湿度的获取、脉搏&血氧数据测量、按键紧急呼叫、LED的亮灭。

 
struct rt_thread led1_thread;
rt_uint8_t rt_led1_thread_stack[128];
void led1_task_entry(void *parameter);

struct rt_thread usart1_thread;
rt_uint8_t rt_usart1_thread_stack[128];
void usart1_task_entry(void *parameter);

struct rt_thread usart2_thread;
rt_uint8_t rt_usart2_thread_stack[128];
void usart2_task_entry(void *parameter);

struct rt_thread button_thread;
rt_uint8_t rt_button_thread_stack[128];
void button_task_entry(void *parameter);
 
//初始化线程函数
void MX_RT_Thread_Init(void)
{
	//初始化LED1线程
rt_thread_init(&led1_thread,"led1",led1_task_entry,RT_NULL,&rt_led1_thread_stack[0],sizeof(rt_led1_thread_stack),3,20);
 rt_thread_init(&usart1_thread,"usart1",usart1_task_entry,RT_NULL,&rt_usart1_thread_stack[0],sizeof(rt_usart1_thread_stack),3,20);
 rt_thread_init(&usart2_thread,"usart2",usart2_task_entry,RT_NULL,&rt_usart2_thread_stack[0],sizeof(rt_usart2_thread_stack),3,20);
 rt_thread_init(&button_thread,"button",button_task_entry,RT_NULL,&rt_button_thread_stack[0],sizeof(rt_button_thread_stack),3,20);
	//开启线程调度
	rt_thread_startup(&led1_thread);
    rt_thread_startup(&usart1_thread);
    rt_thread_startup(&usart2_thread);
    rt_thread_startup(&button_thread);
}
 
//主任务
void MX_RT_Thread_Process(void)
{
	printf("系统正在运行!!!");
	rt_thread_delay(2000);
}
 
//LED1任务
void led1_task_entry(void *parameter)
{
}
//读取温湿度
void usart1_task_entry(void *parameter)
{
}
//读取脉搏&血氧
void usart2_task_entry(void *parameter)
{
}
//按键
void button_task_entry(void *parameter)
{
}

2、配置主函数代码

1、在main,c文件中添加头文件和引入相应函数;
在这里插入图片描述
2、初始化线程和执行主程序
在这里插入图片描述

3、温湿度读取模块(I2C)

具体过程:
通过STM32Cube配置完成基于I2C协议的AHT20温湿度传感器的数据采集

温湿度读取模块这里我们采用的传感器是AHT20、I2C协议。I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地
使用在系统内多个集成电路(IC)间的通讯。

1、上电后等待一段时间待设备正常工作后再读取温湿度:

  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART1_UART_Init();
  uint32_t CT_data[2]={0,0};	
  volatile int  c1,t1;
  rt_thread_delay(50);
  AHT20_Init();
  rt_thread_delay(2500);

2、CRC校验后读取温湿度数据,随后延时5分钟


    AHT20_Read_CTdata_crc(CT_data);  //crc校验后,读取AHT20的温度和湿度数据 
	  c1 = CT_data[0]*100*10/1024/1024;  //计算得到湿度值c1(放大了10倍)
	  t1 = CT_data[1]*200*10/1024/1024-500;//计算得到温度值t1(放大了10倍)	
		printf("湿度:%d%s",c1/10,"%");
	  printf("温度:%d%s",t1/10,"℃");
	  printf("\r\n");
		rt_thread_mdelay(300000);//线程睡眠大约5分钟,挂起执行其他操作

4、LED定时开关灯(pwm)

这里我们使用上面配置过的time2定时器加PWM。

获取日期、时间:

RTC_DateTypeDef Data; //获取日期结构体
RTC_TimeTypeDef Time; //获取时间结构体

HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
	while(1){
		HAL_RTC_GetTime(&hrtc,&Time,RTC_FORMAT_BIN);
		if(Time.Hours == 7&&Time.Minutes == 0&&Time.Seconds == 0){
			for(uint16_t i=1;i<500;i++){//灯渐亮
				htim2.Instance->CCR2 = i;
				rt_thread_delay(5);
			}
		}
		else if(Time.Hours == 22&&Time.Minutes == 0&&Time.Seconds == 0){
			for(uint16_t i=499;i>=1;i--){//灯渐灭
				htim2.Instance->CCR2 = i;
				rt_thread_delay(5);
			}
		}
	}

在初始化之前先获取一次实时时间作为标准:

void getRealTime(void)
{
	HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc, &datebuff, RTC_FORMAT_BIN);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, (uint16_t)datebuff.Year);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (uint16_t)datebuff.Month);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, (uint16_t)datebuff.Date);
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (uint16_t)datebuff.WeekDay);
}
 
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{
	if(hrtc->Instance == RTC)
	{
		getRealTime();	
	}
}

5、按键实现报警信号

因为前面我们随意设置了PB5引脚作为输入,即为按钮,按钮按下发出报警信号,这里我们使用PA4、PA5、PA6引脚的高低电平作为三个按键的报警信号:

按键子线程代码:

while(1){
		switch(KEY_Scan(0))
		{				 
			case KEY1_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_4);
				break;
			case KEY2_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
				break;
			case KEY3_PRES:	
				HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_6);
				break;
			default:
				break;
		}
	}

按键消抖:

uint8_t KEY_Scan(uint8_t mode)
{
	static uint8_t key_up=1;//按键松开标志位
	if(key_up&&((KEY1==0)||(KEY2==0)||(KEY3==0)))
	{
		HAL_Delay(10);//过滤抖动时间
		key_up=0;
		if(KEY1==0)return KEY1_PRES;
		if(KEY2==0)return KEY2_PRES;
		if(KEY3==0)return KEY3_PRES;
	}
	else if(KEY1==1&&KEY2==1&&KEY3==1)key_up=1;
 	return 0;//无按键按下
}

6、脉搏&血氧数据读取

注意:这里的脉搏&血氧仪没使用过,应该也是通过串口采用modbus协议进行通信:

具体的usart通信可参考:通过stm32CubeMX完成一个STM32的USART串口通讯程序
定义脉搏和血氧的地址和变量:

#define REG_SPO2_ADDRESS     0x0001
#define REG_PULSE_RATE_ADDRESS  0x0002
uint16_t spo2_value;
uint16_t pulse_rate_value;

modbus读寄存器请求:

void modbus_handle_read_registers(uint8_t *data, uint16_t len)
{
    uint16_t start_address;
    uint16_t register_count;
    uint16_t i;
    // 解析读寄存器的请求报文
    start_address = (data[2] << 8) | data[3];
    register_count = (data[4] << 8) | data[5];
    // 根据地址和数量读取寄存器数据
    for (i = 0; i < register_count; i++)
    {
        if (start_address + i == REG_SPO2_ADDRESS)
        {
            data[9 + i * 2] = (spo2_value >> 8) & 0xFF;
            data[10 + i * 2] = spo2_value & 0xFF;
        }
        else if (start_address + i == REG_PULSE_RATE_ADDRESS)
        {
            data[9 + i * 2] = (pulse_rate_value >> 8) & 0xFF;
            data[10 + i * 2] = pulse_rate_value & 0xFF;
        }
    }

    // 发送读寄存器响应报文
    uart_send_data(data, 9 + register_count * 2);
}

主循环函数:

void modbus_loop(void)
{
    uint8_t data[256];
    uint16_t len;
    uint16_t new_spo2;
    uint16_t new_pulse_rate;
    while (1)
    {
        // 等待接收数据
        len = uart_receive_data(data, sizeof(data));
        // 处理读寄存器请求
        if (data[0] == 0x01 && data[1] == 0x03)
        {
            modbus_handle_read_registers(data, len);
        }
        // 处理写寄存器请求
        else if (data[0] == 0x01 && data[1] == 0x10)
        {
            // 根据地址和数量读取寄存器数据
            new_spo2 = (data[9] << 8) | data[10];
            new_pulse_rate = (data[11] << 8) | data[12];
            update_spo2_and_pulse_rate(new_spo2, new_pulse_rate);
            // 发送写寄存器响应报文
            uart_send_data(data, 8);
        }
    }
}

定义采样状态:

typedef enum {
    NORMAL,
    CRITICAL
} SamplingState;

SamplingState sampling_state = NORMAL;

定义时间脉搏:

#define NORMAL_SAMPLING_INTERVAL 1800 // 30 分钟
#define CRITICAL_SAMPLING_INTERVAL 10 // 10 秒、算作实时
#define CRITICAL_PULSE_RATE_THRESHOLD 120//脉搏120
#define CRITICAL_SPO2_THRESHOLD 90//血氧90

回调函数实现分状态采样:

void sampling_timer_callback(void)
{
    if (sampling_state == NORMAL)//正常状态
    {
        if (pulse_rate_value > CRITICAL_PULSE_RATE_THRESHOLD || spo2_value < CRITICAL_SPO2_THRESHOLD)
        {
            sampling_state = CRITICAL;
            timer_start(CRITICAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
        else
        {
            // 正常状态下采样
            send_request_to_spo2_pulse_sensor();
            timer_start(NORMAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
    }
    else
    {
        // 危重状态下采样
        send_request_to_spo2_pulse_sensor();
        if (pulse_rate_value <= CRITICAL_PULSE_RATE_THRESHOLD && spo2_value >= CRITICAL_SPO2_THRESHOLD)
        {
            sampling_state = NORMAL;
            timer_start(NORMAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
        else
        {
            timer_start(CRITICAL_SAMPLING_INTERVAL, sampling_timer_callback);
        }
    }
}

7、UART 转 485 接口与 modbus 通信

RS-485通信使用到了串口3,这里发现我们少设置了一个串口,波特率采用9600,开启串口3的发送DMA和接收DMA,都选择普通模式。

modbus协议发送数据:

void RS485_BUS_SendData(u8 *buf, u8 len)
{
	u8 t;
	for(t=0;t<len;t++)
	{
		while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==RESET);
		USART_SendData(UART5,buf[t]);
	}
	
	while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==RESET);
	RS485_BUS_RXCNT = 0;
}

modbus协议接受数据:

void RS485_BUS_ReceiveData(u8 *buf,u8 *len)
{
	u8 rxlen=RS485_BUS_RXCNT;
	u8 i=0;
	*len=0;
	delay_ms(10);
	if((rxlen==RS485_BUS_RXCNT)&&rxlen)				//接收到了数据且接收完成
	{
		for(i=0;i<rxlen;i++)
		{
			buf[i]=RS485_BUS_RXBUF[i];
		}
		*len=RS485_BUS_RXCNT;
		RS485_BUS_RXCNT=0;
	}
}

三、总结

本次系统设计采用了RTthread-nano的多任务框架,一下子使得整个系统的路线清晰了起来,并且还可以设置线程的优先等级,做好整个系统的规划。同时,采用这种多线程的的并发任务处理,每个线程都有自己独立的运行空间,即线程堆栈,每个线程都专注于做自己的事情,互不干涉,独立性高。并且各个线程在使用delay函数时,线程会将任务挂起,自动让出CPU供其他需要的线程进行使用,我们可以放心的在线程里使用延时函数了同时CPU的利用率得到了极大的提高。此次系统的代码我只是根据设计的几个模块,分别划分到多任务处理框架之中,对相应的每个模块的主要代码或部分进行了一个展示,实际里面每一个模块的实现都还需要一定的时间去实现,以及会遇到各种各样的困难。虽然此次,大作业只是进行一个方案设计但内容涵盖了较多本学期的内容,在设计过程中发现有的地方印象已经没有那么深刻就又进行相应部分的复习,再回过头来看发现理解又不一样,感觉还是不错!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【HTML】基础的入门学习

HTML 菜鸟教程 简介 一般结构&#xff1a; <!DOCTYPE html> 声明为 HTML5 文档<html> 元素是 HTML 页面的根元素<head> 元素包含了文档的元&#xff08;meta&#xff09;数据&#xff0c;如 <meta charset"utf-8"> 定义网页编码格式为 ut…

proteus仿真软件中芯片的命名规则与封装方法(详细版)

第一&#xff1a;PCB封装库命名规则 1、集成电路&#xff08;直插&#xff09; 用DIP-引脚数量尾缀来表示双列直插封装​ 尾缀有N和W两种,用来表示器件的体宽​ 为体窄的封装&#xff0c;体宽300mil,引脚间距2.54mm​ 为体宽的封装, 体宽600mil,引脚间距2.54mm​ 如&#…

11、关联数据库

文章目录11、关联数据库11.1 常规方式11.2 常规操作【尚硅谷】idea实战教程-讲师&#xff1a;宋红康 生活是属于每个人自己的感受&#xff0c;不属于任何别人的看法 11、关联数据库 11.1 常规方式 找到数据库选项&#xff1a; 添加指定数据库&#xff1a; 配置MySQL数据库…

5. 网络编程之UDP编程

1. UDP协议的特点 相比与TCP协议来说&#xff0c;UDP协议就显得相对比较简单了。 (1) UDP是无连接的   即发送数据之前不需要建立连接(当然&#xff0c;发送数据结束时也没有连接可释放)&#xff0c;因此减少了开销和发送数据之前的时延。 (2) UDP使用尽最大努力交付   即…

78、Points2NeRF: Generating Neural Radiance Fields from 3D point cloud

简介 github&#xff1a;https://github.com/gmum/points2nerf 由于点云的大小和复杂性&#xff0c;处理这些点云具有挑战性&#xff0c;现有的方法通过将网格拟合到点云并渲染来解决这个问题&#xff0c;这种方法导致结果可视化的保真度降低&#xff0c;并遗漏了在计算机图形…

HashTable HashMap ConcurrentHashMap 的介绍以及区别

目录 &#x1f407;今日良言:投资自己才是最好的投资 &#x1f409;一.HashMap. &#x1f415;二.HashTable &#x1f40d;三.ConcurrentHashMap &#x1f402;四.三者的区别 &#x1f407;今日良言:投资自己才是最好的投资 时隔四十多天,今天博主要更新了. 后续内容也是精…

[机器学习]损失函数DLC

一、损失函数的概念 损失函数(Loss Function)是用于评估预测结果和真实结果之间差距的一个公式&#xff0c;为模型优化指明方向。在模型优化过程中一般表述为&#xff1a;或 与针对整个训练集的代价函数(Cost Function)不同&#xff0c;损失函数通常仅针对单个训练样本。可以归…

RK3568平台开发系列讲解(驱动基础篇)Linux 内核源码介绍

🚀返回专栏总目录 文章目录 一、目录树概览二、快速确定主板关联代码2.1、基础代码2.2、驱动代码沉淀、分享、成长,让自己和他人都能有所收获!😄 📢进行嵌入式 Linux 产品开发,往往需要对内核进行裁剪和定制,以满足嵌入式产品的功能和性能需求。 一、目录树概览 解压…

Python---学生管理系统(pyinstaller)

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 学生管理系统前言创建入口函数新增学生insert展…

行为型模式-迭代器模式

1.概述 定义&#xff1a;提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。 2.结构 迭代器模式主要包含以下角色&#xff1a; 抽象聚合&#xff08;Aggregate&#xff09;角色&#xff1a;定义存储、添加、删除聚合元素以及创建迭代器…

实战案例:Python批量识别银行卡号码并且写入Excel,初学者也可以轻松使用~

大家好&#xff0c;这里是程序员晚枫&#xff0c; 今天我们继续学习Python自动化办公&#xff1a;每次有新员工入职&#xff0c;都要收集大量的工资卡信息&#xff0c;并且生成Excel文档&#xff0c;能不能用Python准确、快速地解决呢&#xff1f; 今天我们就来学习一下&…

【CCNA | 网络模拟器CPT系列】Cisco Packet Tracer 8.2.0 的安装 Ⅰ

目录1. 下载 Cisco Packet Tracer2. 安装 Cisco Packet Tracer&#xff08;1&#xff09;许可协议界面&#xff08;2&#xff09;选择安装目录&#xff08;3&#xff09;选择开始菜单文件夹&#xff08;4&#xff09;选择附加任务&#xff08;5&#xff09;确认设置选择&#x…

K8S环境安装

K8S环境安装 下面是环境的主机名和IP 主机名ipk8smaster192.168.68.150k8snode1192.168.68.151k8snode2192.168.68.152 1、安装docker 配置yum源 sudo yum install -y yum-utils sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/d…

不受支持的 Mac 上的通用控制(现已支持 macOS Ventura)

现已支持 macOS Ventura 请访问原文链接&#xff1a;不受支持的 Mac 上的通用控制&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org 本文为 在不受支持的 Mac 上安装 macOS Ventura、Monterey、Big Sur (OpenCore Legacy Pa…

【Effective_Objective-C_5内存管理】

文章目录前言29.理解引用计数引用计数的工作原理关闭ARC模式引用计数的增减理解一下引用计数存在属性存取方法中的内存管理自动释放池保留环要点30.以ARC简化引用计数使用ARC时必须遵循的方法和命名规则-变量的内存管理语法意义ARC如何清理实例变量覆写内存管理的方法要点31.在…

eclipse调试没反应,没有Debug窗口弹出

用eclipse调试&#xff0c;点击“Run”“Debug as”没有反应&#xff0c;debug视图没有弹出一、打开Preferences点击“Windows”&#xff0c;点击“Preferences”&#xff0c;找到“Run/Debug”下面的“Perspectives”&#xff0c;我的打开是这个样子&#xff0c;要修改一些选项…

【JavaSE成神之路】一文搞定static关键字

哈喽&#xff0c;我是兔哥呀&#xff0c;今天就让我们继续这个JavaSE成神之路&#xff01; 这一节啊&#xff0c;咱们要学习的内容是Java的static关键字。 1. 我们是怎么使用static关键字的 我们其实一直在用static关键字&#xff0c;比如main方法&#xff1a; public stati…

CSS样式基础内容3

目录 CSS三大特性 层叠性 继承性 行高的继承性 优先级 权重的叠加 CSS盒子模型 border边框 边框的复合写法 表格的细线边框 边框会影响盒子的实际大小 内边距 padding会影响盒子实际大小 网页导航案例 外边距 外边距合并 相邻块元素垂直外边距的合并 清除内外…

go语言实战(猜数字+在线词典+服务器)

go语言实战案例1.猜数字游戏2. 词典2.1 request2.2 response2.3 修改写死的单词为用户可写的word2.4 细节优化2.4.1 防止403、404等状态码2.4.2 增强输出可读性2.5 在线词典的最终代码3.SOCKS5代理服务器3.1 tcp echo server3.2 验证3.3 请求3.4 完整代理实现作业1.修改第一个猜…

cadence SPB17.4 S032 - allegro出的槽孔文件不用做任何处理就可以交给板厂生产

文章目录cadence SPB17.4 S032 - allegro出的槽孔文件不用做任何处理就可以交给板厂生产前言备注补充 - CAM350V14.6 - 在win10 22H2下不能正常用ENDcadence SPB17.4 S032 - allegro出的槽孔文件不用做任何处理就可以交给板厂生产 前言 以前交给板厂gerber文件时, 有一次, 板…