简介围绕单片机在水产养殖自动投饵中的应用这份文档给出一套完整的控制系统设计方案适合电子、自动化专业学生做课程设计或毕业设计参考也可供渔业设备开发者借鉴。内容涵盖系统总体设计、硬件电路、软件模块与实验结果分析硬件涉及定时器、存储器、电源管理及电机驱动接口软件包括初始化、参数设置、定时与电机控制程序重点解决传统投饵机定时不准、投饵距离不可调、饲料浪费等问题。压缩包内只含一份doc文档约325KB为全文设计说明条理清晰便于查阅。目前已有215人学习说明该选题在单片机课程设计中具有一定参考热度。通过这份材料读者可以了解以输送减速交流电机和投饵直流电机为控制对象的定时定量投饵实现思路掌握供饵时间、停饵时间、投饵时间与投饵距离等关键参数的设定方法并获得可迁移到其他自动化控制项目的软硬件设计框架与调试参考。1. 从一次错过投喂窗口说起鱼用投饵机自动控制系统在解决什么池塘精养里投喂时间点和单次投饵量直接决定饵料系数与出塘规格人工投喂真正的麻烦不在喂不喂而在能不能每天同一时刻、同一份量喂下去临时出门、夜里起不来、天气突变任何一次落空都会让鱼群抢食不均投多了残饵沉底坏水投少了长势参差。基于单片机的鱼用投饵机自动控制系统要办的事很具体——把投喂动作从人的手表搬到一片单片机上。整套系统通常由主控芯片、定时单元、电机驱动、料仓与抛料机构、按键显示、料位检测六块组成按预设餐次自动启动按设定时长出料缺料或卡料时声光报警。它适合做课程设计和毕业设计的同学用来练手也适合鱼塘经营者判断一台自控投饵机的成本、可靠性与维护边界。2. 投饵机自动控制系统的硬件选型与最小系统搭建投饵机的控制逻辑并不复杂几个定时点触发、一路输出驱动抛料机构、两三路按键、一路料位开关。逻辑简单不代表硬件可以随便画鱼塘现场湿度大、电源波动、电机启停带来的干扰才是决定这套系统能不能连续跑一个养殖周期的关键。2.1 主控选型的取舍STC89C52RC、STC15W 与 STM32做这类设计主控有三条常见路线。STC89C52RC 这类经典 51 是最稳的选择5V 电平、外围简单、Keil C51 资料最全定时器 0/1 足够撑起分钟级计时价格也低缺点是片内 RAM 只有 256 字节、没有独立 EEPROM参数得靠 IAP 模拟存储。STC15W 系列是折中方案同样是 51 内核但单周期指令、速度更快片内带 EEPROM、多路定时器和 PWM省掉外挂存储芯片。STM32 则是在需要彩色屏、图形界面或者后期扩展联网时才值得上成本和开发量都会明显上升。型号内核与主频片内关键资源供电适合投饵机的理由主要限制STC89C52RC8051 / 12MHz 晶振8KB Flash、256B RAM、3 个定时器5V资料多、引脚熟悉、成本低无片内 EEPROMRAM 紧张STC15W408AS8051 单周期 / 最高约 35MHz8KB Flash、片内 EEPROM、多路 PWM5V省掉外挂存储定时精度高封装小手工焊接稍难STM32F103C8T6Cortex-M3 / 72MHz64KB Flash、20KB RAM、硬件 RTC3.3V硬件 RTC 走时准扩展空间大需 3.3V 电平转换开发成本高如果只是按时投饵、按键改参数、数码管显示STC89C52RC 足够写代码时把定时器算准就行要留出记录每餐投饵日志的余地STC15W 系列会更省事。2.2 最小系统在鱼塘环境下的三个注意点第一是晶振。做串口调试就用 11.0592MHz波特率不会偏只做定时投饵、不打算用串口12MHz 更好算一个机器周期正好 1µs定时器重装值可以直接按微秒算。第二是复位常见做法是 10µF 电解电容配 10kΩ 电阻构成上电复位再并一个手动复位按键方便现场强制重启。第三是电源与地抛料电机启动瞬间电流能到安培级会在 5V 轨上砸出尖峰最好让电机电源和单片机电源分开走线、在电机两端并续流二极管、在单片机供电脚附近放 0.1µF 去耦电容必要时加 TVS 管吸收浪涌。提示电机线和信号线不要捆在一起走平行走线越长电机换向时的干扰越容易窜进复位脚表现为一投饵就重启。2.3 驱动链路从单片机 IO 到直流电机与抛料机构单片机 IO 的灌电流能力有限直驱电机既带不动也容易烧口中间必须加一级驱动。小功率直流电机常用 ULN2003 达林顿阵列一路输入对应一路输出内部已集成续流二极管控制 220V 抛料器则用继电器模块光耦隔离后再驱动需要调速的场合可以用 MOS 管加 PWM。下面是电机控制的典型写法#include reg52.h sbit MOTOR_PIN P1^0; /* 抛料电机控制脚接 ULN2003 输入 */ sbit FEED_LED P1^1; /* 投饵状态指示灯 */ /* ULN2003 为反相驱动输出低电平时三极管导通电机得电 */ void Motor_On(void) { MOTOR_PIN 0; FEED_LED 0; /* 共阳接法下低电平点亮 */ } void Motor_Off(void) { MOTOR_PIN 1; FEED_LED 1; }这里的关键点是驱动芯片的反相特性代码里写 0 才是开写 1 是关。如果换成高电平有效的 MOS 驱动电路这两个函数的电平要整体翻过来接线前务必先确认驱动板的逻辑。上电第一次调试时建议先用 LED 代替电机验证电平方向确认无误再插电机避免烧管。2.4 引脚分配表与接线核对引脚规划要在焊板之前定下来否则改代码比改板子便宜得多。以下是 STC89C52RC 上一套常见的分配方案引脚功能外接器件备注P0.0–P0.7段码输出共阴数码管段脚P0 为开漏需 10kΩ 排阻上拉P2.0–P2.3位选输出数码管位脚经三极管或直接驱动P1.0电机控制ULN2003 输入低电平导通P1.1状态指示LED共阳接法P1.2料位检测光电/机械料位开关加 10kΩ 上拉低电平表示缺料P3.2手动投饵键轻触按键低电平触发配合 10kΩ 上拉P3.3参数设置键轻触按键短按切换、长按进入设置P3.0/P3.1串口调试USB-TTL 模块调试期使用量产可省核对时重点查三处料位开关是常开还是常闭决定中断触发沿数码管是共阴还是共阳决定段码表电机额定电压是 5V、12V 还是 220V决定驱动电路形式。这三项在原理图上标清楚后面调试能省下大量时间。3. 定时投饵控制程序的实现从定时器时基到状态机硬件定下来之后软件的难点集中在两件事怎么把晶振的毫秒级节拍累加成分钟级的投饵时间以及怎么让投饵、报警、待机这几个动作互不打架。3.1 用定时器 0 建立 1ms 时基累加出分钟级投饵间隔投饵间隔动辄几十分钟到几小时靠软件延时循环会阻塞按键和显示正确做法是让定时器中断在后台累加。以 12MHz 晶振为例机器周期 1µs定时器 0 工作在模式 116 位定时装 1000 次计数就是 1ms#include reg52.h volatile unsigned int g_ms 0; /* 毫秒计数 */ volatile unsigned char g_sec 0; /* 秒计数 */ volatile unsigned int g_min 0; /* 分钟计数投饵计划基于它 */ void Timer0_Init(void) { TMOD 0xF0; /* 清掉 T0 的模式位 */ TMOD | 0x01; /* T0 模式 116 位定时 */ TH0 (65536 - 1000) / 256; TL0 (65536 - 1000) % 256; /* 12MHz 下 1000 个机器周期 1ms */ ET0 1; /* 允许 T0 中断 */ EA 1; /* 开总中断 */ TR0 1; /* 启动定时器 */ } void Timer0_ISR(void) interrupt 1 { TH0 (65536 - 1000) / 256; /* 必须手动重装模式 1 不自动 */ TL0 (65536 - 1000) % 256; if (g_ms 1000) { g_ms 0; if (g_sec 60) { g_sec 0; g_min; /* 到这一步才是 1 分钟 */ } } }重装值必须写在中断里模式 1 不会自动重载漏写会导致定时周期翻倍。假设用的是 11.0592MHz 晶振机器周期约 1.085µs装 1000 就不足 1ms 了长期累积会明显走慢此时应改装 922 次或者干脆换 12MHz 晶振做纯定时项目。g_min 是无符号整型累到 65535 才溢出按分钟算够跑 45 天配合每日归零逻辑不会出问题。3.2 投饵时长与投饵重量的关系标定表比公式可靠抛料盘转速固定时单位时间出料量近似线性但饵料粒径、含水率、料仓余量都会影响实际出料硬套公式误差很大。稳妥做法是先做一次标定空仓称重装料后连续投 10 秒把抛出的料称重取三次平均得到本机每秒出料克数再折算每次投饵时长。颗粒料在 5mm 抛料盘下的典型标定结果投饵时长实测抛料重量折算速率适用场景3 秒约 62g20.7g/s幼鱼、小规格鱼种6 秒约 124g20.7g/s常规成鱼塘单次投喂10 秒约 205g20.5g/s高密度塘、抢食激烈时15 秒约 298g19.9g/s长时段投喂需注意剩饵速率随时长略降说明长时间投喂时料流会变稀所以程序里给每档配一个查表系数而不是用单一线性公式/* 按投饵时长查表得到实际克重避免线性外推误差 */ unsigned int Feed_Weight(unsigned char seconds) { unsigned int weight; switch (seconds) { case 3: weight 62; break; case 6: weight 124; break; case 10: weight 205; break; case 15: weight 298; break; default: weight 0; break; /* 未标定的档位不投 */ } return weight; }default 分支返回 0 是有意为之如果用户把投饵时长改成了没标定过的档位程序宁可显示 0 让用户重设也不要按错误公式投出去。要增加档位就重新标定后补进 switch。3.3 主循环状态机待机、投饵、报警三态切换投饵机同一时刻只能做一件事用状态机写最清楚。三个状态分别是待机等待下一个餐次、投饵中、报警缺料或卡料所有状态迁移都放在主循环里判断中断只负责改标志位#define ST_IDLE 0 #define ST_FEEDING 1 #define ST_ALARM 2 unsigned char state ST_IDLE; unsigned char feed_seconds 6; unsigned int feed_ticks 0; void main(void) { Timer0_Init(); while (1) { switch (state) { case ST_IDLE: if (Is_Feed_Time()) { /* 到餐次时间了吗 */ if (!Level_Empty()) { /* 有料才投 */ Motor_On(); feed_ticks feed_seconds * 1000; state ST_FEEDING; } else { state ST_ALARM; /* 缺料直接报警 */ } } break; case ST_FEEDING: if (feed_ticks 0) { feed_ticks--; /* 主循环走一圈减一次 */ } else { Motor_Off(); state ST_IDLE; /* 本餐结束回待机 */ } break; case ST_ALARM: Motor_Off(); Alarm_On(); if (!Level_Empty()) { /* 补料后自动复位 */ Alarm_Off(); state ST_IDLE; } break; } } }feed_ticks 的递减节奏取决于主循环一圈的耗时如果主循环里还有显示刷新和按键扫描一圈耗时会大于 1ms导致投饵时间偏长。严谨的写法是把倒计时也放进定时器中断里递减主循环只读状态。调试阶段可以用秒表卡一次 6 秒投饵量出来超过 6 秒就说明主循环太重需要把耗时操作移出主循环。3.4 看门狗与掉电保护无人值守场景的兜底鱼塘边没人守着程序跑飞后如果电机停在通电状态料会一直往下漏。STC89C52RC 内部有看门狗通过 WDT_CONTR 寄存器开启在主循环末尾喂狗一旦跑飞未及时喂狗就自动复位#include reg52.h void WDT_Init(void) { WDT_CONTR 0x35; /* 使能看门狗预分频约 64超时约 1 秒 */ } void WDT_Feed(void) { WDT_CONTR | 0x10; /* 写入喂狗位清计数 */ }参数含义WDT_CONTR 的低三位是预分频值越大超时越长0x35 对应百毫秒到秒级区间具体按晶振调整第 4 位是喂狗清除位写 1 即完成喂狗。喂狗必须放在主循环里如果放在定时器中断里即使主循环卡死中断照常跑看门狗就形同虚设。参数餐次时间、投饵时长要保存到 EEPROM掉电后重新上电继续按原计划走同时用一个标志字节记录本餐是否已投过上电时先读这个标志避免复位瞬间重复投一餐。4. 参数设定与联调时间、投饵量、料位报警怎么调代码烧进去只是开始真正决定这套投饵机好不好用的是参数怎么定、日志怎么看、出问题先查哪。4.1 参数设定每日餐次时间点与单次投饵时长的取值养殖上常见的安排是每日两到四餐按水温和季节调整。夏季水温高鱼摄食旺盛可以早晚各一餐、中午加一餐春秋水温降到 18℃ 以下改成每日一到两餐单次时长也要缩短。参数表里建议同时存时间点和时长两个字段参数编号时间点投饵时长折算重量说明餐次 107:006 秒约 124g早投抢食最积极餐次 212:004 秒约 83g中午少量补充餐次 318:006 秒约 124g晚投日投喂量主餐参数 4———料位报警延时默认 3 秒投饵时长不宜一次调太长单次超过 20 秒后鱼群抢食强度下降后半段基本是无效投喂。料位报警延时是防抖参数料位开关在震动下会瞬时断开延时 3 秒判定为真缺料可以避免投饵过程中的误报警。这几个参数都写在 EEPROM 的固定地址上电先读读不出合法值就用出厂默认值并提示重新设置。4.2 串口调试把每一次投饵打印出来调试期最省事的办法是让单片机把每次投饵动作通过串口发到电脑上看时间点和时长对不对。下面是串口初始化和打印的函数#include reg52.h void Uart_Init(void) { SCON 0x50; /* 模式 1允许接收 */ TMOD | 0x20; /* 定时器 1 模式 28 位自动重装用于波特率 */ TH1 0xFD; /* 11.0592MHz 下 9600bps 的重装值 */ TL1 0xFD; TR1 1; } void Uart_SendByte(unsigned char dat) { SBUF dat; while (!TI); /* 等发送完成 */ TI 0; } void Uart_Log_Feed(unsigned char hour, unsigned char min, unsigned char sec) { Uart_SendByte(F); /* F 表示一次投饵事件 */ Uart_SendByte(hour); Uart_SendByte(min); Uart_SendByte(sec); Uart_SendByte(\n); }TH1 的重装值 0xFD 对应 9600bps如果改用 12MHz 晶振波特率会偏 8% 左右串口助手能收到但是乱码这就是前面强调用串口就配 11.0592MHz的原因。日志里打印的是投饵时刻连续跑两天后对一下电脑时间和日志时间差能直接看出定时漂移有多大。4.3 三类常见故障的排查顺序现场问题往往集中在三处按下面顺序查比到处拆线快得多现象优先检查常用判断方法到点电机不转驱动电平方向、电机供电用 LED 代替电机看 P1.0 是否有电平变化定时越跑越晚晶振频率与重装值是否匹配一天后对比日志时间与标准时间差投饵中途报警料位开关抖动、报警延时太短把延时参数从 1 秒调到 3 秒再观察一投饵就复位电机干扰、电源压降电机电源与单片机电源分开供电验证电机不转时先确认方向再怀疑电机前文代码里 ULN2003 是反相驱动写 1 是关很多人第一次都栽在这一步。定时漂移多数不是程序问题而是 11.0592MHz 晶振配了按 12MHz 算的重装值或者用了廉价晶振温度特性差换成 12MHz 加匹配重装值就能解决。一投饵就复位是典型的电源问题把电机单独用另一路电源供电或者换掉容量不足的 7805 输入端电解电容通常立竿见影。5. 进阶技巧让投饵机可追溯、可校准5.1 把每次投饵写进 EEPROM形成可追溯日志投饵机跑起来之后今天到底投了几餐、每餐投了多少是养殖户最关心的问题。STC89C52RC 没有片内 EEPROM可以用 IAP 方式把 Flash 当数据区或者外挂一片 24C02。日志记录不需要存很多每天存 4 条存满 30 天也就 120 条每条 4 字节小时、分、时长、状态一片 24C02 的 256 字节够用。写入时机放在电机停止之后写一条占用不到 10ms不会影响主循环读的时候按日索引翻页通过按键切换查看比在数码管上滚一遍时间更直观。外挂 EEPROM 的 SCL/SDA 记得各加 4.7kΩ 上拉电阻I2C 在电机干扰下的抗噪能力也会好一些。5.2 用软定时器补偿晶振误差的具体做法晶振有几十 ppm 的固有误差加上 11.0592MHz 和 12MHz 的重装值取整误差一天累积下来差十几秒甚至几分钟是常事。补偿思路很直接在 EEPROM 里存一个日校正秒数程序每天零点根据这个值调整一次 g_min让长期走时对上标准时间。比如实测每天慢 20 秒就让每天零点把分钟计数多减 0.33 分钟或者干脆攒够一定误差值后整分钟跳变一次/* 每过一天调用一次按标定值修正累计计时 */ void Calibrate_Daily(void) { signed char adj EEPROM_Read(ADDR_DAILY_ADJ); /* 每日校正秒数 */ signed int delta adj * 1000 / 60; /* 折算成分钟偏移 */ if (delta 0) { g_min (unsigned int)delta; /* 走慢往前补 */ } else if (delta 0) { g_min - (unsigned int)(-delta); } /* 同时把当天已执行的餐次标志清零 */ EEPROM_Write(ADDR_MEAL_FLAG, 0x00); }校正值怎么得到让机器连续跑 24 小时用日志时间跟标准时间比对差值除以 1 就是单日校正秒数写进 EEPROM 即可。这个方法不依赖更换昂贵的温补晶振在常温鱼塘环境下能把日误差压到 1 秒以内。需要注意的是如果用的是 11.0592MHz 晶振配串口串口波特率是准的走时误差可以通过这种方式单独校正不用为了走时精度牺牲串口稳定性。本文还有配套的精品资源点击获取