
简介这份资源是一份面向水产养殖自动化、机电一体化与单片机课程设计的完整技术文档围绕鱼用投饵机自动控制系统展开针对传统投饵机定时不准、可靠性差、投饵与间歇时间设置不合理、投饵距离无法调节、饲料浪费等痛点提出改进方案。压缩包内仅1个doc文档约325KB内容涵盖课题摘要与英文Abstract、系统整体设计方案、硬件组成与外围电路定时器、存储器、电源管理及电机驱动接口、软件模块划分系统初始化、参数设置、定时控制与电机控制程序以及实验结果与结论并配有目录与中英文关键词属于典型的小型控制系统毕业设计/课程设计参考文本。目前已有215人学习下载可供读者对照参考系统参数设定供饵时间、停饵时间、投饵时间、投饵距离、输送减速交流电机与投饵直流电机的控制思路以及模块化编程的可扩展性设计适合需要撰写同类课题报告或搭建单片机控制方案的学生与工程人员查阅借鉴。1. 鱼塘边那台机器决定成败的其实是投饵时序投饵机这种设备机械部分是现成的料仓、下料盘、抛料电机几十年的成熟结构。真正难的是什么时候转、转多久、一天转几次以及下雨天要不要停。人工投饵的问题不在于人懒而在于不稳定——同样一亩塘今天喂三斤明天喂五斤鱼的摄食节律被反复打乱饵料系数会明显上升残饵还会败坏水质。基于单片机的鱼用投饵机自动控制系统解决的就是这一层控制问题用 MCU 替代人手把「定时 定量 环境联锁」固化下来做到每天同一时刻下料量一致。它适合水产养殖的个体户、合作社也适合拿它做单片机课程设计或毕业设计的同学——外设够全定时器、继电器、显示、按键、传感器逻辑够清晰成本又能压到几十块钱。这一篇按实际能落地的顺序讲先定方案和选型再讲时钟与定时逻辑怎么写然后落到继电器驱动与传感器联锁最后是掉电恢复、堵转检测这类上线后才会想到的问题。热词里出现的 51 单片机、stc 单片机、定时器、继电器、可控硅这些词都会在对应章节说清到底用哪个、为什么。2. 鱼用投饵机自动控制系统的单片机选型与硬件框架2.1 为什么这类项目首选 51 或 STC 而不是 32 位先给结论投饵机的控制任务量极小一天最多十几次定时动作加上几个按键和一段显示对算力没有要求。用 STM32 当然跑得动但要多付稳压、晶振、PCB 层数和开发时间的成本属于典型的过度设计。常见做法是选 STC89C52RC 或 STC15W 系列理由是内置复位电路和看门狗省掉外部的 MAX810Flash 可反复擦写调试阶段烧几十次不心疼5V 供电和继电器模块、LCD1602 直接电平兼容不需要电平转换。热词里提到的「stc 单片机命名规则」这类问题选型时确实用得上。STC 型号里的数字一般表示 Flash 容量和 RAM 大小选型只要保证程序空间够用即可——本项目的代码通常不到 8KBRAM 占用不到 200 字节随便一个 STC15W204 都绰绰有余。如果需要 RS485 组网多台机器再考虑带双串口的型号。要区分两类需求单机独立运行51 足够如果要接手机 App、要联网上报那才需要换带 Wi-Fi 模组的方案但那已经不是「基于单片机的投饵机」这个题目的范围了。2.2 系统框架与主要外设清单一个能上线的投饵机控制系统框图可以拆成五块主控、人机交互、时钟、执行、检测。模块典型器件接口方式数量主控STC89C52RC / STC15W408AS—1实时时钟DS1302三线 SPI 模拟1显示LCD16024 位数据模式1按键独立按键上拉 消抖4执行继电器模块 或 光耦 双向可控硅IO 驱动12料位检测光电对管 / 微动开关输入中断或查询1掉电存储AT24C02I2C 模拟1时钟芯片单独列出来是有原因的。单片机内部定时器走时依赖晶振精度普通 11.0592MHz 晶振一天累积误差能到几十秒一个月差几分钟对一天三次投饵来说虽然能忍但用户会投诉「机器越走越不准」。加一颗 DS1302 配 32.768kHz 晶振走时误差降到每月几秒而且带纽扣电池后断电不掉时间这个成本不到两块钱非常值得。提示如果完全不想加时钟芯片也可以用 12MHz 晶振 定时器中断累计但必须定期用外部手段校准比如连续按三次某个键进入「校时模式」。2.3 硬件连接的关键约束主控到执行器之间绝对不能让单片机 IO 直接驱动电机。IO 口输出电流通常在 20mA 以内而抛料电机启动瞬间电流可能到 1A 以上直连的结果就是烧口甚至烧芯片。正确做法是两级IO 先驱动三极管或光耦再由三极管驱动继电器线圈继电器触点接电机回路。如果用电可控硅方案注意过零触发和散热。热词里的「单片机控制可控硅电路图」常见的 MOC3021 BTA16 组合就是干这个的适合频繁启停的场合因为没有继电器触点火花寿命更长。一个容易忽略的点电机是感性负载断开瞬间会产生反向电动势。继电器线圈两端必须反并一只续流二极管1N4148 或 1N4007触点侧建议并联 RC 吸收网络否则跳火会干扰单片机导致复位。很多「莫名其妙重启」的板子问题就出在这里。# 硬件自检阶段建议做的几件事用万用表/示波器 # 1. 上电后测主控 VCC 是否为 5.0V ± 0.2V # 2. 电机不接时测继电器驱动三极管基极电压投饵时应有 0.7V 左右 # 3. 电机启动瞬间用示波器看 VCC 是否有超过 0.3V 的跌落 # 4. 断开电机重复投饵动作 100 次观察是否出现复位看初始画面这四步做完基本能把电源和干扰问题提前筛掉避免后面写代码时怀疑人生。3. 定时投饵逻辑的实现DS1302 走时与定时器中断3.1 DS1302 读写时序的代码实现DS1302 用的是三线串行接口CE、I/O、SCLK没有硬件 SPI 的 51 单片机需要用 IO 模拟时序。网上能搜到的驱动很多但不少在写字节时没有在时钟下降沿前后留足够的延时换一批芯片就通信失败。#include reg52.h sbit DS_CE P1^0; // 片选低电平时芯片处于复位状态 sbit DS_IO P1^1; // 双向数据线 sbit DS_SCLK P1^2; // 串行时钟 /* 向 DS1302 写一个字节LSB 先发 */ void DS1302_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS_IO dat 0x01; // 先放数据位 DS_SCLK 1; // 上升沿DS1302 采样 DS_SCLK 0; // 拉低准备下一位 dat 1; } } /* 读取一个字节 */ unsigned char DS1302_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; if (DS_IO) dat | 0x80; // 下降沿后数据有效 DS_SCLK 1; DS_SCLK 0; } return dat; } /* 写寄存器cmd 为命令字dat 为待写值 */ void DS1302_WriteReg(unsigned char cmd, unsigned char dat) { DS_CE 0; DS_SCLK 0; DS_CE 1; DS1302_WriteByte(cmd); // 命令字必须先发 DS1302_WriteByte(dat); DS_CE 0; } /* 读寄存器 */ unsigned char DS1302_ReadReg(unsigned char cmd) { unsigned char dat; DS_CE 0; DS_SCLK 0; DS_CE 1; DS1302_WriteByte(cmd); dat DS1302_ReadByte(); DS_CE 0; return dat; }逻辑说明DS1302 的通信规则是先发一个命令字节包含地址和读写方向位再传数据。数据是低位先出LSB first这一点和常见的 SPI 相反写反了就永远读到 0xFF。CE 拉高期间整个传输必须连续完成中途拉低会被当作传输中止。参数说明命令字的 bit0 是读写位1 表示读0 表示写bit1 到 bit6 是寄存器地址bit7 固定为 1。比如读秒寄存器是 0x81写秒寄存器是 0x80。BURST 模式用 0xBE/0xBF可以一次性把时、分、秒全部读出来实际项目里用 BURST 更省事。注意DS1302 寄存器存的是 BCD 码不是二进制。也就是说 23 分存进去是 0x23 而不是 0x17读取后要转换才能做数值比较这个坑几乎每个新手都会踩一次。3.2 投饵时刻的比较逻辑与定时器中断最朴素的写法是主循环里每秒读一次时间然后跟预设的时刻表比较。这种写法能用但有隐患如果某一秒因为下料动作被阻塞比如等待料位传感器返回可能整秒被跳过导致漏投。稳妥做法是把「判断」和「执行」分开。定时器中断只负责维护一个秒计数标志主循环检测标志置位后再处理业务逻辑业务逻辑本身不阻塞超过几十毫秒。volatile unsigned char g_sec_flag 0; volatile unsigned int g_ms_tick 0; /* Timer0 初始化12MHz 晶振1ms 中断一次 */ void Timer0_Init(void) { TMOD 0xF0; TMOD | 0x01; // 方式116 位定时器 TH0 0xFC; // 65536 - 1000 64536 0xFC18 TL0 0x18; ET0 1; EA 1; TR0 1; } void Timer0_ISR(void) interrupt 1 { TH0 0xFC; TL0 0x18; g_ms_tick; if (g_ms_tick 1000) { // 累计到 1 秒 g_ms_tick 0; g_sec_flag 1; } } /* 主循环中的投饵判断 */ void Feed_Check(void) { static unsigned char last_sec 0xFF; unsigned char h, m, s; if (!g_sec_flag) return; g_sec_flag 0; rtc_read_time(h, m, s); // 从 DS1302 读并转成十进制 if (s last_sec) return; // 同一秒内不重复判断 last_sec s; /* 遍历预设的 N 组投饵时刻 */ for (unsigned char i 0; i FEED_SLOT_NUM; i) { if (g_slot[i].enable g_slot[i].hour h g_slot[i].min m s 2) { // 只在整分的前两秒触发防抖 Feed_Start(g_slot[i].duration); } } }逻辑说明定时器中断只做计数不碰任何外设保证中断执行时间极短业务判断放在主循环即使某次处理耗时长了下一秒的标志仍然会被捕获不会漏掉投饵时刻。last_sec是防止主循环跑得太快在同一分钟内重复触发。参数说明s 2这个条件是关键。如果写成s 0主循环恰好在那一秒没跑到就会错过放宽到前两秒触发窗口更大同时last_sec保证只会触发一次。g_slot是投饵时刻表数组hour和min存十进制值读 DS1302 后必须先做 BCD 到十进制的转换再比较。3.3 投饵时长与料量的标定投饵量不是靠传感器称重而是靠「电机转多久」来近似。这要求出厂或安装时做一次标定让下料电机转 10 秒称一下落料重量得到克每秒系数。参数含义典型值调整方式FEED_SEC_PER_100G每 100 克所需秒数3.5 s标定后写入 EEPROMFEED_MIN_SEC最短投饵时长2 s低于此值电机可能不启动FEED_MAX_SEC单次最长投饵时长120 s防止堵转时一直转FEED_INTERVAL两次投饵最小间隔30 min避免用户误操作连投有了这个系数用户在界面上设置「每次 500 克」程序自动换算成 17 秒。这个做法比让用户直接输入秒数友好得多也是实际产品里最常见的处理。4. 执行机构驱动与传感器联锁的工程细节4.1 继电器与可控硅两种驱动方案的取舍前面提过执行器有两条路继电器和可控硅。选哪个取决于电机类型和启停频率。对比项继电器方案可控硅方案成本低25 元中515 元寿命触点约 10 万次无机械磨损干扰触点跳火需 RC 吸收较小驱动三极管 续流二极管光耦 MOC3021适用交流电机、启停不频繁交流电机、频繁启停缺点长期大电流触点易粘连需散热片过零控制复杂投饵机一天启停十几次继电器完全够用。但要注意触点额定电流要留两倍余量电机额定 1A就选 5A 触点以上的继电器否则用半年触点就会粘连表现为「停止投饵后电机还在转」这是很危险的故障——料会全部倒进塘里。可控硅方案适合一天投饵几十次的集约化养殖。这里有个细节双向可控硅必须过零触发否则在电压峰值导通会产生很强的电磁干扰能把旁边的单片机直接打复位。MOC3021 是随机相位光耦MOC3063 才是过零型两者不能混用。/* 继电器驱动低电平有效加互斥保护 */ sbit RELAY_FEED P2^0; void Feed_Start(unsigned char sec) { if (sec FEED_MIN_SEC) sec FEED_MIN_SEC; if (sec FEED_MAX_SEC) sec FEED_MAX_SEC; g_feed_remain sec; // 剩余秒数由定时器递减 RELAY_FEED 0; // 吸合 g_feed_state 1; } void Feed_Stop(void) { RELAY_FEED 1; // 释放 g_feed_remain 0; g_feed_state 0; } /* 在主循环中调用负责倒计时和收尾 */ void Feed_Task(void) { if (!g_feed_state) return; if (g_sec_flag) { g_sec_flag 0; if (g_feed_remain 0) { g_feed_remain--; } if (g_feed_remain 0) { Feed_Stop(); } } }逻辑说明投饵启动时把秒数写入g_feed_remain主循环每秒递减一次减到 0 就断电。这样做的好处是投饵时长和主循环的其他任务解耦即使期间有按键操作也不会影响计时。参数说明FEED_MIN_SEC和FEED_MAX_SEC的限幅非常必要。下限防止用户设 0.5 秒导致电机堵转烧毁上限防止堵转时电机持续运转。实际代码里还应该加「连续运转超过上限立即停机并报警」的保护。4.2 料位、雨量与水温的联锁条件纯定时的投饵机在雨天会做无用功——饵料撒下去鱼不吃全沉底坏水。加一个雨滴传感器或者更简单一个继电器输入接雨控开关就能解决。联锁逻辑用状态机写最清晰条件来源动作料仓缺料光电对管被遮挡停止投饵显示「缺料」正在下雨雨滴传感器 / 雨控开关本次跳过顺延到下一时段水温低于 15℃DS18B20自动把投饵量减半电机堵转电流检测 / 转速反馈立即停机蜂鸣器报警水温这一条容易被忽视。淡水鱼在低水温下代谢变慢摄食量大幅下降同样的投饵量会造成大量残饵。用 DS18B20 读水温按温度区间动态调整投饵时长是低成本高收益的优化。DS18B20 是单总线器件读写时序对延时敏感如果在中断里调用很容易读失败。正确做法是在主循环里发起转换等待 750ms 后再读结果期间不要关中断。4.3 掉电保存与参数恢复用户设置好的投饵时刻表、投饵量断电后必须保留否则每次停电都要重设产品没法用。两种存储方式一是用 AT24C02I2C 接口2Kbit 容量存几十组时刻表没问题擦写次数 100 万次。写入时注意页边界AT24C02 一页 8 字节跨页写会回卷覆盖必须分页处理。写完要给 5ms 左右的内部写周期时间期间不响应任何命令。二是用 STC 单片机自带的 EEPROMIAP 功能不需要外挂芯片省两个 IO 和一块 PCB 面积。热词里「中颖单片机 eeprom 程序」也是类似思路各家 IAP 寄存器不同但流程一致解锁、擦除扇区、写入、上锁。/* AT24C02 写一个字节含页内地址处理 */ void AT24C02_WriteByte(unsigned char addr, unsigned char dat) { I2C_Start(); I2C_SendByte(0xA0); // 器件地址 写 I2C_WaitAck(); I2C_SendByte(addr); // 字地址 I2C_WaitAck(); I2C_SendByte(dat); I2C_WaitAck(); I2C_Stop(); Delay_ms(6); // 等待内部写周期完成必须留够 }参数说明0xA0是 AT24C02 的写地址读地址是0xA1由 A0/A1/A2 引脚决定全接地时是 0xA0。最后那个 6ms 延时不能省写周期内发起新通信会失败表现为「偶尔存不进去」。如果使用页写模式提升速度一次最多写 8 字节且不能跨 8 字节边界。提示调试存储功能时建议加一条串口打印把写入和读回的值都打出来对比。存储类 bug 靠肉眼查代码很难发现靠数据对比一眼就看出来了。5. 上线后的验证与三个容易翻车的技巧5.1 用串口日志做无显示调试板子装进机箱后就看不到 LCD 了出问题只能靠猜。留一路串口9600 波特率8 位数据无校验在调试期打日志是性价比最高的手段。定时器 1 做波特率发生器注意要关掉它的中断只用溢出率。void Uart_Init(void) { SCON 0x50; // 方式1允许接收 TMOD 0x0F; TMOD | 0x20; // Timer1 方式28 位自动重装 TH1 0xFD; // 9600bps 11.0592MHz TL1 0xFD; TR1 1; ES 1; // 开串口中断 EA 1; } void Uart_SendStr(char *s) { while (*s) { SBUF *s; while (!TI); TI 0; } }逻辑说明在Feed_Start、Feed_Stop、缺料报警、按键处理这些关键节点各插一句Uart_SendStr把时间和状态打出来。跑几天后把日志拉出来看投饵次数对不对、有没有漏投、报警触发了几次一目了然。参数说明TH1 0xFD对应 11.0592MHz 晶振下 9600 波特率。如果用 12MHz 晶振这个值会偏差 8.5%通信会出错所以只要是带串口的 51 项目晶振必须选 11.0592MHz 而不是 12MHz这是很多人第一次做串口时踩的坑。5.2 看门狗与卡死的现场判断STC 单片机内置看门狗开启后如果主循环卡住不喂狗就会复位。但看门狗是双刃剑开得太激进正常的延时会被误判为卡死。建议喂狗间隔设为 1 秒以上主循环里跑一遍业务不超过 100ms 的情况下绰绰有余。判断是否发生过看门狗复位可以读 STC 的复位标志寄存器或者更土的办法——在初始化时往 EEPROM 里累加一个计数。跑一周后把这个计数读出来如果一直在涨说明有周期性卡死要重点查电机干扰和电源。5.3 投饵机安装现场的三个经验值第一继电器输出到电机的线尽量短且和信号线分开走平行走线超过半米基本会出干扰。第二控制板电源单独一路不要和电机共用同一个 5V哪怕多花几块钱加个隔离电源模块也值得。第三所有对外接口加 TVS 管或压敏电阻鱼塘边雷击和静电很常见一颗几毛钱的管子能救回整块板。投饵时刻的设置上实际经验是每天两到三次、固定时间、每次间隔不少于 4 小时比一天五次少量投喂更省料。让程序里支持「节假日模式」——比如某天不出塘就整机跳过——用长按某个按键触发这种小功能用户会觉得顺手。真正决定这套系统好不好用的往往不是定时精度到秒而是这些现场用得上的细节。本文还有配套的精品资源点击获取