简介学生选修课程系统设计是面向计算机及相关专业学生与C课程设计初学者的一份完整课程设计报告围绕学生登录、课程浏览、选课与退选、个人信息管理、成绩查询和数据统计等功能展开并覆盖需求分析、模块图与流程图、关键代码、调试分析、用户手册与设计总结等实践环节。压缩包共1个doc文件约259KB以Word文档形式集中呈现选题背景、功能设计、实现思路与测试记录便于对照课程设计任务书梳理报告结构与编码重点。已有1444人学习说明其在C面向对象课程设计场景中具有一定参考价值。读者可从中了解用户认证、课程信息管理、选课冲突检查、数据持久化等模块的组织方式也能借鉴问题分析、性能说明、改进设想和文档编写方法适合作为课程设计报告撰写与系统实现的对照材料。1. 一套控制台选课系统的真实样子答辩前一晚机房机器上程序一跑就闪退查了半天发现是写文件后没close。这份学生选修课程系统设计属于 c课程设计 里最经典的一类题目控制台菜单、三个数据类、一个manage管理类、三份 txt 数据文件教师端管学生和课程的增删改查与统计学生端只做课程浏览和选课。整套代码几百行单个 cpp 文件就能编译跑通适合刚学完类、继承和文件流的同学拿来做骨架也适合照着改出自己的版本。2. 类结构拆解三个数据类加一个 manage 大管家先把模块图落到代码上。报告里的模块图是「主模块 → 学生端 / 教师端 / 退出」学生端下面挂「显示课程、选课、退出」教师端下面挂「添加、查询、显示、修改、删除、统计、读取、保存」。这套分层在控制台程序里最终只体现为两类东西数据类负责存一条记录管理类负责把一堆记录按功能处理掉。2.1 student、course、xuanke三个只存数据的类代码里student和course都是纯数据类成员全是public加两个方法put_xxx()负责从cin读一条display()负责往cout打一行。这种写法在工程里会被骂但在课程设计里非常实用——它把「读一条记录」和「打印一条记录」的格式固定在类内部增删改查十几个函数就不用各写一遍cin 序列。class student { public: // 数据成员全公开图省事也埋下了被随意改的隐患 int SID; // 学号唯一键所有查找都靠它 char name[10]; // 姓名定长字符数组别指望存长名字 char sex; // 性别单个字符 M/F int age; char SDEPT[10]; // 系别 char grade[10]; // 班级 char TEL[10]; // 联系方式 public: void put_student() { cout 请输入学生的 学号、姓名、性别、年龄、系别、班级、联系方式:\n; cin SID name sex age SDEPT grade TEL; } void display() { cout SID \t name \t sex \t age \t SDEPT \t grade \t TEL endl; } };cin name对char[]的行为是「跳过空白、读到下一个空白为止」所以姓名里带空格会被截断这是后面所有「显示不全」问题的源头之一。name[10]的容量也要心里有数GBK 编码下一个汉字占 2 字节最多塞 4 个汉字加\0UTF-8 下占 3 字节只能塞 3 个汉字。写测试数据时随手敲个「欧阳建国」就要出事。course类字段是课程代码、名称、性质、总学时、学分、开课学期另外多了一个int vis[90];。这个vis全篇没有任何地方用到明显是早期想做「每门课被谁选过」的标记数组后来改用xuanke类就忘了删。看到这种字段不要直接删——先把全文搜一遍确认无引用再删否则容易连编译都过不去。xuanke只有两个intc_num课程代码和s_num学号。它本质上是一条关系记录不是实体所以没有display()也没有唯一性检查。这个设计决定了选课数据只能追加、不能去重第五章会专门说它带来的坑。2.2 manage 的多重继承画类图好看写代码难受manage的声明是全文最值得停下来看一眼的地方class manage : public student, public course, public xuanke { private: student st[maxn]; // 又持有 student 数组 course co[maxn]; xuanke xk[maxn]; public: void c_read(); void s_read(); void add_course(); void add_student(); // ... 其余功能函数 };它同时继承student、course、xuanke又各自声明了一个数组。继承来的那份成员SID、c_code之类在整个程序里一次都没用过manage对象本身只当容器使。这么写唯一的好处是类图上「manage 继承三个类」的箭头很好看符合课程设计报告里对「继承与派生」的考察点。代价是实打实的一是manage对象凭空多出十几个无用字段二是三个父类的put_xxx()、display()都被继承进来调用时容易出现m.display()这种到底打谁的歧义三是后面想把student换成std::string字段得连带改父类。常见的改法是换成组合把三个数组包进来就行功能一行不用动class manage { private: student st[maxn]; course co[maxn]; xuanke xk[maxn]; int c_cnt 0, s_cnt 0; // 顺手把全局计数收进来见 2.3 public: // 功能函数不变 };如果你正在写报告、需要体现继承可以保留manage的多重继承但把数组改成继承来的那份别再声明一遍如果只是想让程序干净点直接组合。2.3 定长数组与全局计数maxn100 的天花板容量靠一个全局常量卡死const int maxn 100; int c_cnt 0, s_cnt 0; // 课程数、学生数全局变量所有数组都是st[maxn]、co[maxn]所有功能函数直接读写这两个全局计数。好处是函数签名干净add_course()不需要参数也不需要返回值坏处是没人管边界。看add_course的写入分支if (i c_cnt) // 找到同号课程说明重复 cout 有重复添加失败 endl; else { co[c_cnt] c; // 直接往后追加没判断 c_cnt 是否已经到 maxn cout 添加成功 endl; }第 101 门课就会写到数组外面去控制台程序未必立刻崩可能只是把s_cnt或别的全局变量踩坏表现成「加了课之后学生人数莫名其妙变了」这种玄学现象。补一行判断就能挡住else if (c_cnt maxn) cout 课程数量已达上限 maxn 请先清理数据 endl; else { co[c_cnt] c; cout 添加成功 endl; }一个功能落到哪些函数、动哪份数据可以先理成一张表改的时候不容易漏功能函数操作对象落盘时机读取c_read/s_readco[]/st[]程序启动时添加add_course/add_studentco[]/st[]手动save查询c_search/s_search按编号线性扫描不落盘显示c_display/s_display全量遍历不落盘修改c_edit/s_edit先备份再覆盖手动save删除c_delete/s_delete后续元素前移手动save统计cnt计数 读选课文件不落盘选课xuankxk立即fwrite读懂这张表基本就掌握了这套代码的数据流向内存里的对象数组是主副本txt 是快照选课记录是个例外绕开了内存数组直接写文件。3. 一次操作的完整链路从菜单到文件落盘控制台程序的骨架就三件事菜单循环、分派到功能函数、退出前把内存写回文件。这套代码在前两件事上做对了第三件留了个大坑。3.1 主循环为什么必须用 while 包住菜单报告里明确记了一条问题「进入教师端时如果选择某项操作操作完成后就会退出系统」。原因很直白——main里如果用if分派执行完一个分支就走到return 0。修法是把菜单和分派都塞进while用一个专属的退出值当条件。原文只给了主流程图a1 学生端、a2 教师端、a3 退出函数名按报告里的命名还原出来大致是这样int main() { manage m; m.c_read(); // 先把两份数据文件读进对象数组 m.s_read(); // 之后所有操作都在内存里做不再碰文件 int a 0; while (a ! 3) // 3 是退出循环保证一次操作完还能回主菜单 { cout 1.学生端 2.教师端 3.退出 endl; cin a; if (a 1) { int b 0; while (b ! 2) // 学生端二级菜单1 显示课程 2 退出 { cout 1.查看课程 2.选课 3.返回 endl; cin b; if (b 1) m.c_display(); else if (b 2) m.xuank(); } } else if (a 2) { int c 0; while (c ! 9) // 教师端二级菜单9 返回 { cout 1.添加课程 2.添加学生 3.查询 4.显示 5.修改 6.删除 7.统计 8.保存 9.返回 endl; cin c; switch (c) { case 1: m.add_course(); break; case 2: m.add_student(); break; case 7: m.cnt(); break; case 8: m.save(); break; // ... 其余分支 } } } } return 0; // 注意这里没有调 save()退出即丢弃修改 }几个参数值得说清楚。外层a的三个取值是硬编码的改动菜单顺序就得同步改判断工程里一般用枚举或常量替代。内层b、c的退出值分别是 2 和 9跟外层 3 不冲突这是刻意的——如果都用 3从二级菜单返回时会直接把外层循环也干掉。switch里每个case都写了break漏一个就会「删完课程接着执行统计」这种 fall-through 在答辩现场非常致命。return 0之前没有save()是这套代码最贵的教训报告里第 7 条「下次再读取文件时发现上次修改的记录没有显示其原因是当运行完程序时没有进行保存」说的就是它。修法有两种省事的一种是在main返回前补一行m.save();稳妥的一种是用atexit注册或者在每次增删改之后立刻落盘。3.2 读与存c_read / s_read 和 save 必须成对读文件用ifstream加运算符靠「读到文件尾就失败」结束循环void c_read() // 从 course_information.txt 读课程记录到对象数组 { ifstream in; in.open(course_information.txt, ios::in); if (!in) { cout 打开失败! endl; return; // 文件不存在就当没有数据不能让流程断掉 } while (in co[c_cnt].c_code co[c_cnt].c_name co[c_cnt].c_nature co[c_cnt].c_hour co[c_cnt].c_grade co[c_cnt].c_semester) { c_cnt; // 每成功读满一条计数加一 } in.close(); // 必须关否则下一次打开同一文件会失败 }while (in ...)的语义是把整条记录的六个字段依次读进数组的当前槽位六个都读成功才算表达式为真。如果某一行只写了五个字段第六个读失败c_cnt不会自增那条半截数据就被丢弃程序不会崩也不会提示——所以 txt 里每行的字段数必须严格一致。ios::in是只读模式文件不存在时open失败代码直接return而不报死这是对的行为第一次运行本来就没有数据文件。写回用的是ofstream加ios::outios::out默认截断等于全量覆盖void save() // 把对象数组里的内容写回两份 txt { ofstream out(course_information.txt, ios::out); if (!out) { cout 打开失败! endl; return; } for (int i 0; i c_cnt; i) { out co[i].c_code co[i].c_name co[i].c_nature co[i].c_hour co[i].c_grade co[i].c_semester endl; } out.close(); // 先关课程文件 out.open(student_information.txt, ios::out); // 同一个流对象复用 if (!out) { cout 打开失败! endl; return; } for (int i 0; i s_cnt; i) { out st[i].SID st[i].name st[i].sex st[i].age st[i].SDEPT st[i].grade st[i].TEL endl; } out.close(); }这里有个写法上的依赖字段之间用空格分隔读的时候也按空白切所以格式天然自洽。代价是任何字段内部都不能有空格课程名写成「数据结构 与算法」就会把整行读歪。要支持空格得改用getline加分隔符比如逗号或竖线读的时候也要相应改成getline两边必须同时改只改一边直接完蛋。提示save()是「全量覆盖」它会把文件里原有的内容整个替换成内存里数组的内容。如果程序启动时c_read()失败文件被占用、路径不对数组是空的此时再点保存原有的数据就被清空了。我一般会在save()开头加一句c_cnt 0 s_cnt 0的确认提示。3.3 增删改三个函数共用一套「线性扫描 唯一性检查」增删改查四个动作里查最简、改最容易出错。先看添加核心是查重void add_course() { course c; c.put_course(); // 先读进临时对象不污染数组 int i; for (i 0; i c_cnt; i) // 线性扫一遍找同号 if (co[i].c_code c.c_code) break; // i 停在下标上说明重复 if (i c_cnt) cout 有重复添加失败 endl; else co[c_cnt] c; // 只有确认不重复才落进数组 }「先读进临时对象检查通过再入数组」是一个值得固化的习惯。反过来写——先co[c_cnt].put_course()再检查 ——一旦重复数组尾部就留了一条脏数据还得回滚。删除用的是顺序表经典套路找到下标后面元素依次前移计数减一void c_delete() { int c_num, i; cout 请输入要删除课程的代码: ; cin c_num; for (i 0; i c_cnt; i) if (co[i].c_code c_num) break; if (i c_cnt) { cout 没有该项记录 endl; return; } for (int j i; j c_cnt - 1; j) // 注意上界是 c_cnt-1最后一位不用搬 co[j] co[j 1]; c_cnt--; // 计数减一尾部那条变成逻辑上的垃圾 cout 记录删除成功 endl; }循环上界写成j c_cnt - 1而不是j c_cnt是为了避免co[c_cnt - 1] co[c_cnt]读到数组有效区之外。这个边界错一次删除最后一条记录时就会把一个越界对象拷进来后面显示随机乱码。修改是三处里唯一需要「后悔药」的报告里也专门记了这条坑void c_edit() { int c_num, i; cout 请输入要修改课程的代码: ; cin c_num; for (i 0; i c_cnt; i) if (co[i].c_code c_num) break; if (i c_cnt) { cout 没有此项记录 endl; return; } course backup co[i]; // 关键改之前先备份原记录 co[i].put_course(); // 直接把新内容读进数组原内容已被覆盖 int flag 0; for (int j 0; j c_cnt; j) { if (j i) continue; // 跳过自己否则永远和自己重复 if (co[j].c_code co[i].c_code) { flag 1; break; } } if (flag) { co[i] backup; // 撞号了用备份还原 cout 存在相同编号的课程修改失败 endl; } else cout 修改成功 endl; }backup这一步不能省。put_course()是直接往co[i]的字段里写的写完原始数据就没了如果此时发现新编号和别的课程撞了没有备份就还原不回去只能让用户重新录一遍。这个「先备份、后覆盖、失败回滚」的套路在编辑场景里通用写数据库更新也是一个思路。4. 选课与统计两套持久化方案的并轨学生端只有两个动作看课程、选课。看课程复用c_display()选课却单开了一条路——绕开内存数组直接用FILE*写二进制。4.1 xuank 为什么用 fwrite 而不是 ofstreamvoid xuank() { FILE *fp; xuanke xk; cin xk.c_num xk.s_num; // 课程代码 学号构成一条选课记录 if ((fp fopen(xuanke_information.txt, ab)) NULL) // 追加模式打开 if ((fp fopen(xuanke_information.txt, wb)) NULL) // 打不开就新建 { cout 不能建立 xuanke_information.txt 文件 endl; exit(0); } fseek(fp, 0, 2); // 定位到文件末尾 fwrite(xk, sizeof(xuanke), 1, fp); // 整块写一条记录 cout 选课成功! endl; fclose(fp); }选这个方案的理由报告里写得很实在一开始想用「每门课挂一个链表存学号」读文件时很麻烦索性单开一个定长的xuanke类加一份二进制文件。sizeof(xuanke)是两个int8 字节每条记录长度固定这给统计带了巨大方便——文件总字节数除以 8 就是记录条数不用逐行解析。代价也明显。二进制文件用记事本打开是乱码课程设计验收时老师想直接看一眼数据都看不见ab模式本身就能创建文件所以里层那次fopen是冗余的写fopen(..., ab)一行就够最要紧的是xuank()完全没做校验——课程代码存不存在不管、同一个学生重复选同一门课也不管。补校验不难void xuank() { int c_num, s_num; cin c_num s_num; int i; for (i 0; i c_cnt; i) if (co[i].c_code c_num) break; if (i c_cnt) { cout 课程不存在选课失败 endl; return; } // 查重先扫一遍已有的 xk 数组需要把选课记录也读进内存 for (int k 0; k xk_cnt; k) if (xk[k].c_num c_num xk[k].s_num s_num) { cout 已经选过这门课 endl; return; } xk[xk_cnt].c_num c_num; xk[xk_cnt].s_num s_num; xk_cnt; // 落盘交给统一的 save()别再单独 fwrite }要做到这一步前提是把选课记录也像课程、学生一样在启动时读进xk[]数组让三种数据用同一套机制。这属于二次开发里性价比最高的一处改造。4.2 统计某门课的选课人数ftell 除以 sizeof统计函数用一个switch分三个分支学生人数、课程门数、某门课的选课名单。前两个直接读全局计数第三个才真正读文件void cnt() { int a; cin a; switch (a) { case 1: cout 学生人数为: c_cnt endl; // 注意这里用的是 c_cnt break; case 2: cout 课程门数为: s_cnt endl; // 这里用的是 s_cnt break; case 3: { FILE *fp fopen(xuanke_information.txt, rb); if (fp NULL) { cout 文件打开失败 endl; exit(0); } int c_num, n, count 0, A[60]; cin c_num; // 要统计的课程代码 fseek(fp, 0, 2); n ftell(fp) / sizeof(xuanke); // 文件字节数 ÷ 记录长度 记录条数 rewind(fp); // 回到文件头才能顺序读 xuanke t; for (int i 0; i n; i) { fread(t, sizeof(xuanke), 1, fp); if (t.c_num c_num) A[count] t.s_num; // 命中就存学号 } cout 选修该课程的人数为: count endl; for (int i 0; i count; i) cout A[i] ; fclose(fp); break; } } }ftell拿当前偏移已经fseek到末尾所以是文件总长除以sizeof(xuanke)得到记录数这是定长二进制文件最省事的计数方式比一行行读快得多也不怕文件末尾多一个换行。rewind(fp)不能漏它等价于fseek(fp, 0, SEEK_SET)漏了就一条都读不到count恒为 0。A[60]是个裸奔的定长数组选课人数超过 60 就写越界。改法要么开大一点并加边界判断要么用std::vectorint直接push_back。另外int count 0;这个局部变量名跟标准库的std::count撞了虽然这里没using namespace std之外的影响实际有using namespace std;在文件头部但属于自己给自己下绊子改叫hit更安全。case 1和case 2那两行有个真实的 bug统计学生人数输出的是c_cnt课程计数统计课程门数输出的是s_cnt学生计数两个变量写反了。这种错误在测试时很容易漏因为如果你先加 3 门课、再加 3 个学生两个数字一样看不出问题一旦数量不等就露馅。验收前务必造不对等的数据测一遍。5. 调试与避坑五条上机血泪经验的代码对照报告里列了七条上机问题挑出最有代表性的五条按「现象 → 原因 → 解决」重新捋一遍都是能直接对到代码行的。5.1 存出来的文件是乱码现象用fwrite把课程记录写进 txt用记事本打开全是方块和乱码。原因fwrite写的是内存的二进制镜像int占 4 字节、char[10]占 10 字节全是原始字节记事本按文本解码当然看不懂。解决换成 C 的ofstream加输出字段之间用空格分隔就是 3.2 里save()的写法。只要程序自己读得回来、人能看懂用文本格式比二进制格式划算得多。xuanke_information.txt保留二进制是另一回事——它只给程序读不给人看而且定长记录让统计变简单。5.2 文件明明存在读的时候却打不开现象c_read()里in.open(...)失败但去文件夹里看文件确实在。原因上一次save()或写操作结束时没有close()文件句柄还挂着再次打开就失败。解决所有open必须有配对的close包括提前return的分支。稳妥写法是在每个函数末尾和所有错误返回前都调一次或者干脆封装个 RAII 小类构造时打开、析构时关闭。5.3 改了编号撞车原记录也被改没了现象修改课程信息时填了一个已存在的编号程序提示「修改失败」但回去一看那条记录的内容已经变成新填的了。原因co[i].put_course()直接往数组元素里写原数据当场被覆盖后面的查重是在覆盖之后做的发现重复时已经没有原值可还原。解决改之前先course backup co[i];查重失败就co[i] backup;回滚。3.3 里的c_edit()就是补好的版本。这个模式记住一句话任何「先写后校验」的地方都要先留一份快照。5.4 新加的记录显示不出来现象添加了课程返回列表看最后一条永远是旧的。原因添加时只写了co[c_cnt] c;忘了c_cnt。数组那一位确实有数据但所有遍历都按c_cnt做上界第c_cnt位永远不会被访问到。解决统一用co[c_cnt] c;这种「先取下标再自增」的写法别拆成两句。同理删除时c_cnt--也不能忘否则列表末尾会多出一条幽灵记录。5.5 关掉程序再打开改动全丢了现象加了一堆数据退出程序重新运行全没了。原因main里return 0之前没调save()。内存里的数组只在内存里不落盘就是一次性的。解决最省事是在main退出前补m.save();更稳的是每次增删改成功后立刻落盘代价是频繁 IO。折中方案是在菜单里留一个「保存」项并把它做成退出前的必经步骤同时在退出提示里写清楚「未保存的修改将丢失」。注意还有一个隐蔽的坑没在上面五条里——cin 读int时如果用户敲了字母流会进入失败状态之后所有cin都失效程序开始疯狂打印菜单。修法是在每次读失败后cin.clear(); cin.ignore(1024, \n);。控制台程序被输入搞死的情况一半是这个原因。6. 二次开发把它改成能拿得出手的版本这套代码改起来有几个明确的着力点按投入产出排序。6.1 先做三处小改造第一把student、course里的char[]全换成std::stringname长度、中文截断、空格截断这些问题一次性消失代价是c_read里的要改成getline配分隔符。第二把全局的c_cnt、s_cnt收进manage类做成员顺手在每个写入分支加上 maxn的判断。第三把case 1、case 2里写反的两个计数变量换回来用「3 门课 5 个学生」这种不对等数据测一遍。6.2 结构上补两块选课进内存、加退选现在的选课是「立即 fwrite、统计时再读文件」三种数据两套机制维护起来别扭。改法是在manage里加一个xuanke xk[maxn]; int xk_cnt 0;程序启动时从xuanke_information.txt读进数组选课时先查课程存在、再查是否已选、然后才追加save()里增加第三段写回。这样选课就成了内存操作退选也就顺理成章——找到xk[i]里匹配的(c_num, s_num)后面元素前移、xk_cnt--跟c_delete()完全同构。6.3 怎么验证改对了不要靠肉眼扫菜单。给每种操作配一组固定数据两门同号课程测添加查重、一条改号撞车的记录测回滚、一条删除中间记录后的列表测前移、一次重复选课测去重、一次退出重进测持久化。跑完一轮去 txt 里数行数、对字段比在控制台里翻屏可靠得多。// 快速自检把内存状态打出来和 txt 对一遍 void dump() { cout 课程 c_cnt 门学生 s_cnt 人选课记录 xk_cnt 条 endl; for (int i 0; i xk_cnt; i) cout xk[i].s_num - xk[i].c_num endl; }我最早做这类作业时也栽过同一个坑数据加完直接关窗口第二天打开一片空白白干两小时。从那以后我每次写完增删改都强制走一遍「操作 → 保存 → 关程序 → 重开 → 看列表」的闭环绝不靠「应该存上了」这种自我安慰。这份代码本身不难难的是把这些细节一次性考虑全。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
前端大数据渲染优化:虚拟列表原理与实战 1. 当10万条数据砸向前端时会发生什么?第一次遇到这种需求时,我正喝着咖啡调试页面。后端同事突然说:"这个接口要返回10万条商品数据,你们前端处理下展示。"咖啡差点喷在屏幕上——这相当于让浏览器一次性加载一本300页… · 2026/11/2 17:46:39
Hindsight:LLM API调用审计与回溯系统 1. 项目概述:Hindsight 不是“事后诸葛亮”,而是一套可落地的 LLM 操作审计与回溯系统你有没有遇到过这样的场景:线上服务突然返回一堆401 Unauthorized,日志里只有一行incorrect api key provided: sk-svcac****,但你… · 2026/11/2 17:46:39
算法刷题方法论:从新手到高手的进阶指南 1. 为什么我们需要持续刷算法题上周面试一位三年经验的开发,让他手写个二分查找都卡壳半小时。这让我想起自己刚入行时,被算法支配的恐惧——直到把《算法导论》刷了三遍才找到感觉。现在每天保持2道题的节奏,五年下来累计刷题量突破3000&… · 2026/11/2 17:46:39
xmake使用总结 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 18:36:28
C++享元结构文档样式应用 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 18:36:28
QML调用JS /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 18:36:28
基于MAX14431与PIC18F86K90的工业SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 18:35:27
ESP32+VESC的BLDC牵引控制单元设计与实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 18:35:27
PLFM_RADAR:开源相控阵雷达全栈实践指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 18:35:27
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18
雷达信号调制技术解析:从LFM到伪线性调频原理与应用 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15
基于MAX14431与TM4C123的工业SPI隔离电路设计全攻略 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15
PLFM_RADAR:相位线性调频雷达信号处理框架 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18