C8051空压机控制软件源码解析:从温度采样到继电器输出 简介这份资源是面向工业自动化初学者与嵌入式开发者的空压机控制软件工程包基于C8051微控制器与C语言实现重点解决压缩机运行中温度、压力实时监测与安全保护问题。包内共151个文件以69个h头文件、39个inc包含文件、12个c源文件为核心代码另有obj、lst编译中间文件、lib库文件、uvproj与uvopt工程配置、hex烧录文件及少量jpg截图压缩包约2.66MB结构完整可直接导入Keil环境编译研究。软件通过ADC采集温度与压力传感器信号设定阈值触发过温保护与加载卸载调节并借助UART、SPI及LCD或LED实现人机交互与远程数据交换还包含Flash读写、按键处理、系统调度等模块便于理解实际项目代码组织方式。目前已有634人学习下载适合希望掌握C8051外设驱动、传感器集成与压缩机控制逻辑的读者参考借鉴。1. 空压机控制软件从传感器采样到继电器输出的完整闭环空压机控制软件听起来像是工业现场才用得上的东西但真把它拆开看核心逻辑并不复杂一颗 C8051 单片机定时读温度传感器跟设定阈值比较决定压缩机启停、风扇转停同时把状态码刷到数码管或串口屏上。这套逻辑跑在螺杆机、活塞机、涡旋机的控制板上管的是排气温度、油温、电机过载这些要命的参数。我手上这份源码包就是一套典型的空压机控制器固件C8051F 系列 MCU 打底NTC 热敏电阻做温度采集继电器组驱动接触器和风扇带 RS485 上传。适合谁看做工业控制固件的、接非标设备改造的、想拿一套能跑通的 C8051 项目练手的都能从里面抠出能直接复用的模块。温度采样这块的玄学在于 NTC 非线性查表还是算 Steinhart-Hart代码里怎么处理后面细说。2. 硬件抽象层拆解C8051 外设初始化与温度采样链路2.1 系统时钟与 ADC 配置C8051F 系列跟 STM32 不一样它的交叉开关和优先权交叉开关是绕不开的坎。这份代码里系统时钟配到 24.5MHz 内部振荡器ADC0 用定时器 2 溢出触发采样率设在 1kHz 左右。为什么不用软件查询因为空压机现场干扰大软件查询容易采到开关噪声定时器触发能保证采样间隔均匀后面做滑动滤波才有意义。// 系统时钟与ADC初始化C8051F340 void SysClk_Init(void) { OSCICN 0x83; // 使能内部振荡器不分频24.5MHz CLKSEL 0x00; // 时钟源选内部振荡器 while((OSCICN 0x10) 0); // 等待振荡器稳定 } void ADC0_Init(void) { REF0CN 0x03; // 使能内部基准VREF2.4V AMX0P 0x0C; // P3.0作为ADC0正输入接NTC分压 ADC0CF 0xF8; // 转换时钟系统时钟/8增益1 ADC0CN 0x02; // 定时器2溢出触发右对齐 EIE1 | 0x08; // 使能ADC0转换结束中断 AD0EN 1; // 使能ADC0 }这里有个参数要盯住ADC0CF 0xF8把 SAR 时钟设成 3MHz 左右24.5MHz 系统时钟下转换一次大概 2.5us留足了采样保持时间。AMX0P 0x0C选的是 P3.0 通道对应 NTC 分压点。如果你换板子这个通道号得跟着原理图改改错了采回来就是满量程或者零别问我是怎么知道的。2.2 NTC 温度换算查表加线性插值NTC 10K 的阻值-温度曲线是非线性的直接套公式算浮点对 C8051 来说太奢侈。代码里用的是分段查表加线性插值表按 5℃ 一档存 ADC 码值实际温度落在两档之间就插值。这样既省 CPU精度也能做到 ±0.5℃ 以内对空压机排气温度保护来说够用了。// NTC温度查表-20℃到150℃5℃一档 const unsigned int NTC_Table[] { 3720, 3510, 3280, 3040, 2790, 2540, 2290, 2050, 1820, 1600, 1400, 1220, 1060, 920, 790, 680, 580, 495, 420, 355, 300, 253, 213, 180, 152, 128, 108, 91, 77, 65, 55, 47, 40, 34, 29 }; int Get_Temperature(unsigned int adc_val) { unsigned char i; int temp; // 超出表格范围直接返回边界值 if(adc_val NTC_Table[0]) return -20; if(adc_val NTC_Table[34]) return 150; for(i 0; i 34; i) { if(adc_val NTC_Table[i1] adc_val NTC_Table[i]) { // 线性插值温度 低档温度 比例 * 5℃ temp -20 i * 5; temp (int)((long)(NTC_Table[i] - adc_val) * 5 / (NTC_Table[i] - NTC_Table[i1])); return temp; } } return 0; }NTC_Table数组是按 ADC 码值从大到小排的因为温度越高 NTC 阻值越小分压点电压越低ADC 码值越小。插值那行用long强转是为了防止乘法溢出C8051 的 int 是 16 位(NTC_Table[i] - adc_val) * 5最大能到 18000 多不转 long 会翻车。这个表你得根据自己用的 NTC 型号重新标定B 值不同曲线差很多直接抄表温度能偏出十几度。2.3 滑动滤波与断线检测ADC 采回来的原始值不能直接用现场接触器吸合那一下能干扰出几十个码值的跳动。代码里做了 8 点滑动平均同时判断采样值是否在合理范围内超出范围认为传感器断线直接报故障停机。#define FILTER_LEN 8 unsigned int Adc_Buf[FILTER_LEN]; unsigned char Buf_Index 0; unsigned int Filter_Adc(unsigned int new_val) { unsigned long sum 0; unsigned char i; Adc_Buf[Buf_Index] new_val; Buf_Index (Buf_Index 1) % FILTER_LEN; for(i 0; i FILTER_LEN; i) { sum Adc_Buf[i]; } return (unsigned int)(sum / FILTER_LEN); } // 断线检测ADC值持续为满量程或零 bit Check_Sensor_Open(unsigned int adc_val) { static unsigned char open_cnt 0; if(adc_val 4000 || adc_val 20) { open_cnt; if(open_cnt 50) { // 连续50次异常约50ms open_cnt 0; return 1; // 传感器断线 } } else { open_cnt 0; } return 0; }滤波长度 8 是我试出来的平衡点再长响应就迟钝了排气温度涨得快的时候保护会滞后。断线检测的阈值 4000 和 20 对应分压电路的上拉和下拉极限你要是改了分压电阻这两个数得跟着调。open_cnt 50是防抖避免干扰脉冲误报。3. 控制逻辑实现阈值比较、状态机与继电器输出3.1 压缩机启停状态机空压机的启停不是简单的大于开小于关得有回差、有延时、有故障锁死。代码里用了一个四状态机待机、启动延时、运行、故障。温度超过上限进入启动延时延时到了才吸合接触器温度降到下限以下再延时停机防止频繁启停把接触器烧了。typedef enum { STATE_IDLE 0, STATE_START_DELAY, STATE_RUNNING, STATE_FAULT } SystemState; SystemState sys_state STATE_IDLE; unsigned int delay_cnt 0; void Control_Logic(int temp) { switch(sys_state) { case STATE_IDLE: if(temp TEMP_START) { // 超过启动阈值 sys_state STATE_START_DELAY; delay_cnt 0; } break; case STATE_START_DELAY: delay_cnt; if(delay_cnt START_DELAY_MS) { // 启动延时到 RELAY_MAIN 1; // 吸合主接触器 RELAY_FAN 1; // 风扇同时启动 sys_state STATE_RUNNING; } if(temp TEMP_START - TEMP_HYST) { // 温度回落取消启动 sys_state STATE_IDLE; } break; case STATE_RUNNING: if(temp TEMP_STOP) { // 低于停机阈值 RELAY_MAIN 0; RELAY_FAN 0; sys_state STATE_IDLE; } if(temp TEMP_FAULT) { // 超温故障 RELAY_MAIN 0; RELAY_FAN 0; sys_state STATE_FAULT; } break; case STATE_FAULT: RELAY_MAIN 0; RELAY_FAN 0; // 需要手动复位或断电重启才能退出 if(FAULT_RESET_BTN 0 temp TEMP_START) { sys_state STATE_IDLE; } break; } }TEMP_START、TEMP_STOP、TEMP_FAULT这三个阈值一般设 85℃、75℃、105℃回差TEMP_HYST给 5℃。启动延时START_DELAY_MS通常 3000 到 5000ms看电机功率。故障状态必须手动复位这是血泪教训——自动复位的话传感器真坏了会反复启停把机头干废。3.2 继电器输出与互锁继电器输出这块有个坑C8051 的 IO 口上电默认是高阻如果继电器驱动电路是低电平有效上电瞬间会全部吸合一下。代码里在初始化最前面就把所有继电器口置高硬件上再加下拉电阻。// 继电器引脚定义低电平有效 sbit RELAY_MAIN P1^0; // 主接触器 sbit RELAY_FAN P1^1; // 风扇 sbit RELAY_ALARM P1^2; // 报警输出 void Relay_Init(void) { P1MDOUT | 0x07; // P1.0-P1.2推挽输出 P1 | 0x07; // 先全部置高继电器不动作 RELAY_MAIN 1; RELAY_FAN 1; RELAY_ALARM 1; } // 互锁风扇故障时禁止启动主电机 bit Check_Interlock(void) { if(RELAY_FAN 0 FAN_FEEDBACK 1) { return 0; // 风扇没转起来不允许启动主机 } return 1; }P1MDOUT设推挽是为了驱动能力够直接推光耦或者三极管。互锁逻辑里FAN_FEEDBACK是风扇接触器的辅助触点没有这个反馈风扇皮带断了主机还在转机头几分钟就过热。这个互锁在代码里是硬判断不经过状态机任何情况下风扇没反馈都不让主机吸合。3.3 故障码存储与串口上传故障码存在片内 Flash 里掉电不丢。C8051F340 有 4KB 的 Flash 可以当 EEPROM 用代码里划了最后 512 字节存故障记录每条记录 8 字节循环覆盖。#define FAULT_BASE 0xF000 // 故障记录起始地址 #define FAULT_MAX 64 // 最多存64条 typedef struct { unsigned char code; // 故障码 int temp; // 故障时温度 unsigned int runtime; // 累计运行时间 unsigned char reserved; } FaultRecord; void Save_Fault(unsigned char code, int temp) { unsigned char i; FaultRecord rec; unsigned int addr; rec.code code; rec.temp temp; rec.runtime total_runtime; // 找空位或覆盖最旧 for(i 0; i FAULT_MAX; i) { addr FAULT_BASE i * sizeof(FaultRecord); if(Flash_Read_Byte(addr) 0xFF) { break; } } if(i FAULT_MAX) i 0; // 满了从头覆盖 addr FAULT_BASE i * sizeof(FaultRecord); Flash_Write(addr, (unsigned char *)rec, sizeof(FaultRecord)); }Flash_Write是底层擦写函数C8051 写 Flash 前必须先擦除整个扇区所以实际代码里是先把整个扇区读到 RAM改完再擦再写回去。这个操作耗时大概 20ms期间不能断电否则记录全丢。我一般会在写之前先把看门狗喂了再关中断写完再开。4. 避坑与排查C8051 空压机控制器的常见翻车点4.1 温度跳变导致频繁启停现象是空压机运行几分钟就停停了没一会又启接触器啪啪响。原因多半是 NTC 采样线跟电机线走在一起了变频器或者接触器线圈的干扰耦合进来ADC 值跳变超过回差。解决分两步硬件上把 NTC 线换成屏蔽线屏蔽层单端接地软件上把滑动滤波长度从 8 加到 16同时在状态机里加一个“温度必须连续 3 次超过阈值才动作”的判断。4.2 上电继电器瞬间吸合前面提过C8051 的 IO 上电默认高阻如果驱动电路是低有效上电瞬间继电器会抖一下。现象是每次上电空压机“啪”一声接触器吸合又断开。解决是在main函数最开头、任何外设初始化之前先把继电器口置高同时硬件上加 10K 下拉电阻到地。代码顺序不能反先配P1MDOUT再置高否则推挽没开置高也没驱动能力。4.3 Flash 写故障记录导致看门狗复位C8051 擦写 Flash 的时候 CPU 会停顿如果看门狗没处理直接复位。现象是每次存故障记录就重启故障码还存不进去。解决是在Flash_Write前后加WDT_Feed()并且把看门狗超时设到最长。更稳妥的做法是存故障前先关看门狗存完再开但这样万一写 Flash 卡死就没救了我一般还是喂狗。4.4 RS485 通信在电机启动时丢包现象是空压机一启动上位机就报通信中断。原因是 RS485 收发器跟电机共地地电位跳变导致差分信号超范围。解决是给 RS485 加隔离模块或者至少把 A/B 线换成双绞屏蔽线屏蔽层接大地。软件上把通信超时从 1s 放宽到 3s给重传留时间。4.5 温度查表精度不够有人直接抄了 10K B3950 的表结果实际用的 NTC 是 B3435温度差出 8℃ 以上。现象是排气温度显示 95℃实际才 87℃保护提前动作。解决是拿恒温槽标定从 20℃ 到 120℃ 每隔 10℃ 记 ADC 值重新生成表格。没有恒温槽的话至少用冰水混合物和沸水标两个点反推 B 值修正。5. 二次开发改造从单机控制到多机联控的扩展思路拿到这份源码想改造成自己的东西最直接的路子是先把温度采样和继电器输出这两个模块吃透然后按自己的硬件改引脚和阈值。但如果你要做多台空压机联控就得动架构了。我一般会这么干把单机控制逻辑封装成一个独立的任务函数主循环里用时间片轮询每台机器一个状态机实例通过 RS485 或者 CAN 交换启停请求和故障状态。// 多机联控的调度框架 #define MAX_UNITS 4 typedef struct { SystemState state; int temp; unsigned int delay_cnt; unsigned char fault_code; unsigned char addr; // 从站地址 } CompressorUnit; CompressorUnit units[MAX_UNITS]; void Main_Loop(void) { unsigned char i; static unsigned int tick 0; for(i 0; i MAX_UNITS; i) { // 每10ms调度一台4台轮一圈40ms if((tick % MAX_UNITS) i) { units[i].temp Get_Temperature( Filter_Adc(ADC_Read(units[i].addr))); Control_Logic_Unit(units[i]); } } tick; // 联控逻辑根据总管压力决定启停台数 if(tick % 100 0) { Cascade_Control(units, MAX_UNITS); } }这个框架的关键是把Control_Logic改成带实例参数的版本所有状态变量从全局挪到结构体里。Cascade_Control负责读总管压力压力低于下限就按运行时间最短的优先启动高于上限就按运行时间最长的优先停。轮询周期 40ms 对空压机来说绰绰有余温度变化是秒级的。改造时验证方法很简单先用信号发生器模拟 NTC 分压从 0.5V 扫到 2.0V看每台机器的状态切换和继电器动作是否跟单机版一致。然后接实际传感器用热风枪加热看联控逻辑能不能正确加减载。最后跑 72 小时老化重点盯 Flash 故障记录有没有丢、RS485 有没有累积错误。从那以后我每次改完控制逻辑都强制走一遍“模拟传感器全量程扫描 继电器动作记录 故障注入”这三步少一步都不敢上机。希望帮到你。本文还有配套的精品资源点击获取