grbl源代码protocal部分

serial 收到 G代码命令后,要通过 protocal 来处理

protocol.h

#ifndef protocol_h

#define protocol_h

//行执行串行输入流的缓冲区大小。 线缓存器大小来自于可执行串口输入流

//注意:不是一个问题,除了极端的情况下,但线缓冲区大小太小了和G代码块可以截断。正式,G代码标准支持多达256

//字符。在未来的版本中,这将会增加,当我们知道多少额外

//内存空间我们可以投资或重写刀位点解析器没有这个

//缓冲区。

#ifndef LINE_BUFFER_SIZE

#define LINE_BUFFER_SIZE 80

#endif

//开始Grbl主循环。它处理所有从串口输入的字符并执行

//他们完成。它还负责完成初始化过程。

void protocol_main_loop();

//检查和执行实时命令在主程序的各种停止点

void protocol_execute_realtime();

.

// void protocol_cycle_start(); //通知步进子系统开始执行刀位点程序缓冲区。

// Reinitializes the buffer after a feed hold for a resume.

// void protocol_cycle_reinitialize(); //重新启动缓冲后饲料保持一份简历。

// void protocol_feed_hold();   //启动一个提要的正在运行的程序

//执行自动循环功能,如果启用。

void protocol_auto_cycle_start();

//阻塞,直到所有缓冲的步骤执行

void protocol_buffer_synchronize();

#endif

_____________________________________________________________________________________________

实现代码实际上是一个封装代码没有具体实现代码

预先解析定义不同的注释类型。

#define COMMENT_NONE 0

#define COMMENT_TYPE_PARENTHESES 1

#define COMMENT_TYPE_SEMICOLON 2

static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. G代码缓存

//指导和执行一行从protocol_process格式化的输入。虽然大多

//输入流刀位点,这也指导和执行Grbl内部命令,

//设置等发起寻的周期,和切换开关状态

static void protocol_execute_line(char *line)

{

protocol_execute_realtime(); // Runtime command check point.

if (sys.abort) { return; } // Bail to calling function upon system abort  保释调用函数时系统中止

#ifdef REPORT_ECHO_LINE_RECEIVED //选择打印接收路线

report_echo_line_received(line);

#endif

if (line[0] == 0) { // 第一个字节等于0表示空行或者是注释行

// Empty or comment line. Send status message for syncing purposes.

// 空或注释行。发送状态信息进行同步。

report_status_message(STATUS_OK);

} else if (line[0] == '$') { //$表示系统命令,执行sys_execute_line()并打印报告信息

// Grbl '$' system command

report_status_message(system_execute_line(line));

} else if (sys.state == STATE_ALARM) {

// Everything else is gcode. Block if in alarm mode.

report_status_message(STATUS_ALARM_LOCK);

} else {

// Parse and execute g-code block!

report_status_message(gc_execute_line(line)); // 执行G代码解析

}

}

___________________________________________________________________________________________

下面是grbl 循环

void protocol_main_loop()

{

//完成初始化过程在一个电或复位。

// Print welcome message

report_init_message();

// 检查和报告报警状态重置后,错误,初始化上电。

if (sys.state == STATE_ALARM) {

report_feedback_message(MESSAGE_ALARM_LOCK);

} else {

// All systems go! But first check for safety door.

if (system_check_safety_door_ajar()) {

bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);

protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state.

} else {

sys.state = STATE_IDLE; // Set system to ready. Clear all state flags.

}

system_execute_startup(line); // Execute startup script.

}

// ---------------------------------------------------------------------------------

// Primary loop! Upon a system abort, this exits back to main() to reset the system.

// ---------------------------------------------------------------------------------

uint8_t comment = COMMENT_NONE;

uint8_t char_counter = 0;

uint8_t c;

for (;;) {

//一行输入的串行数据的过程,作为数据变得可用。执行一个

//初始过滤去除空间和评论和利用所有的信件。

//注意:评论时,空间和块删除(如果支持的话)处理技术

//在刀位点解析器,它有助于压缩到Grbl传入的数据

//线缓冲区,这是有限的。刀位点标准实际上州一行不行

//超过256个字符,但是Arduino Uno没有内存空间。

//有更好的处理器,它会很容易把这个初步解析的

//分离任务共享的刀位点解析器和Grbl系统命令。

while((c = serial_read()) != SERIAL_NO_DATA) {

if ((c == 'n') || (c == 'r')) { // 回车

line[char_counter] = 0; // 设置结束

protocol_execute_line(line); // Line is complete. Execute it!

comment = COMMENT_NONE;

char_counter = 0;

} else {

if (comment != COMMENT_NONE) {

// Throw away all comment characters 去掉注释

if (c == ')') {  //c==41

// End of comment. Resume line. But, not if semicolon type comment.

if (comment == COMMENT_TYPE_PARENTHESES) { comment = COMMENT_NONE; }

}

} else {

if (c <= ' ') {

// c<20去掉空格和控制字符

} else if (c == '/') {

//块删除不支持。忽略字符。

//注意:如果支持,只需要检查系统是否启用了块删除。

} else if (c == '(') {

// Enable comments flag and ignore all characters until ')' or EOL.

// NOTE: This doesn't follow the NIST definition exactly, but is good enough for now.

// In the future, we could simply remove the items within the comments, but retain the

// comment control characters, so that the g-code parser can error-check it.

comment = COMMENT_TYPE_PARENTHESES;

} else if (c == ';') {

// NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.

comment = COMMENT_TYPE_SEMICOLON;

// TODO: Install '%' feature

// } else if (c == '%') {

// Program start-end percent sign NOT SUPPORTED.

// NOTE: This maybe installed to tell Grbl when a program is running vs manual input,

// where, during a program, the system auto-cycle start will continue to execute

// everything until the next '%' sign. This will help fix resuming issues with certain

// functions that empty the planner buffer to execute its task on-time.

} else if (char_counter >= (LINE_BUFFER_SIZE-1)) {

// Detect line buffer overflow. Report error and reset line buffer.

report_status_message(STATUS_OVERFLOW);

comment = COMMENT_NONE;

char_counter = 0;

} else if (c >= 'a' && c <= 'z') { // 小写转大写

line[char_counter++] = c-'a'+'A';

} else {

line[char_counter++] = c;

}

}

}

}

// If there are no more characters in the serial read buffer to be processed and executed,

// this indicates that g-code streaming has either filled the planner buffer or has

// completed. In either case, auto-cycle start, if enabled, any queued moves.

protocol_auto_cycle_start(); //系统开始循环

protocol_execute_realtime();  // Runtime command check point. 运行实时命令

if (sys.abort) { return; } // Bail to main() program loop to reset system.

}

return;

}

____________________________________________________________________________________________

protocol_execute_realtime()函数主要是是设置各种状态

void protocol_execute_realtime()

{

uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.

do { // If system is suspended, suspend loop restarts here.

// Check and execute alarms.

rt_exec = sys.rt_exec_alarm; //拷贝全局各自报警标志位变量到rt_exec

if (rt_exec) { // 如果rt_exec为真进入

//系统报警。一切都已经关闭了已经严重错误的东西。报告

//用户错误的来源。如果重要,Grbl禁用通过输入死循环直到系统重置/中止。

sys.state = STATE_ALARM; // 设置系统为报警状态

if (rt_exec & EXEC_ALARM_HARD_LIMIT) {

report_alarm_message(ALARM_HARD_LIMIT_ERROR); //报告报警信息为接近极限值

} else if (rt_exec & EXEC_ALARM_SOFT_LIMIT) { //报告执行软件限位报警

report_alarm_message(ALARM_SOFT_LIMIT_ERROR); //报告出现软件限位报警

} else if (rt_exec & EXEC_ALARM_ABORT_CYCLE) { //执行停止循环报警

report_alarm_message(ALARM_ABORT_CYCLE); //出现终止循环报警

} else if (rt_exec & EXEC_ALARM_PROBE_FAIL) { //执行探查失败报警

report_alarm_message(ALARM_PROBE_FAIL); //出现探查失败报警

} else if (rt_exec & EXEC_ALARM_HOMING_FAIL) { //执行返回原点失败报警

report_alarm_message(ALARM_HOMING_FAIL); //出现返回原点失败报警

}

// Halt everything upon a critical event flag. Currently hard and soft limits flag this.

// 停止一切在一个关键事件标志。目前硬和软限制旗帜。

if (rt_exec & EXEC_CRITICAL_EVENT) { //如果系统是循环实际进入

report_feedback_message(MESSAGE_CRITICAL_EVENT); //报告反馈信息

bit_false_atomic(sys.rt_exec_state,EXEC_RESET); //清除目前的复位状态

do {

// Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits

// typically occur while unattended or not paying attention. Gives the user time

// to do what is needed before resetting, like killing the incoming stream. The

// same could be said about soft limits. While the position is not lost, the incoming

// stream could be still engaged and cause a serious crash if it continues afterwards.

// TODO: Allow status reports during a critical alarm. Still need to think about implications of this.

//         if (sys.rt_exec_state & EXEC_STATUS_REPORT) {

//           report_realtime_status();

//           bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);

//         }

} while (bit_isfalse(sys.rt_exec_state,EXEC_RESET)); //((x & mask) == 0)

}

bit_false_atomic(sys.rt_exec_alarm,0xFF); // 清除所有报警标志

}

// Check amd execute realtime commands /校验和执行实时命令

rt_exec = sys.rt_exec_state; //拷贝全局各自报警标志位变量到rt_exec

if (rt_exec) { // Enter only if any bit flag is true

// Execute system abort.

if (rt_exec & EXEC_RESET) {

sys.abort = true;  // Only place this is set true.

return; // Nothing else to do but exit.

}

// Execute and serial print status

if (rt_exec & EXEC_STATUS_REPORT) {

report_realtime_status();

bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);

}

// Execute hold states.

// NOTE: The math involved to calculate the hold should be low enough for most, if not all,

// operational scenarios. Once hold is initiated, the system enters a suspend state to block

// all main program processes until either reset or resumed.

//执行保持状态。

//注意:所涉及的数学计算持有应该足够低对大多数人来说,即使不是全部,

//操作场景。一旦启动,系统进入暂停状态主程序流程,直到重置或恢复。

//如果全局各自报警标志位其中(执行取消动作) | (执行进给保持) | (执行安全门)任意一位为真进入

if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {

// TODO: CHECK MODE? How to handle this? Likely nothing, since it only works when IDLE and then resets Grbl.

//待办事项:检查模式?如何处理呢?可能没有,因为它只会在空闲,然后重置Grbl。

// State check for allowable states for hold methods. 状态检查容许状态的方法。

//系统为闲着状态, 开始循环状态, 回原点状态, 控制取消状态, 开始保持状态, 开始安全门状态时进入

if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_MOTION_CANCEL | STATE_HOLD | STATE_SAFETY_DOOR))) {

// If in CYCLE state, all hold states immediately initiate a motion HOLD.

//如果在循环状态,所有保持状态立即启动运动。

if (sys.state == STATE_CYCLE) { //如果是循环状态执行暂停状态

st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.通知持有减速步进模块来验算。

sys.suspend = SUSPEND_ENABLE_HOLD; // Initiate holding cycle with flag.

}

// If IDLE, Grbl is not in motion. Simply indicate suspend ready state.

if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_ENABLE_READY; }

// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle

// to halt and cancel the remainder of the motion.

//执行和标志和减速运动取消并返回空闲。主要由探测使用周期停止和取消剩余的运动。

if (rt_exec & EXEC_MOTION_CANCEL) {

// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand

// to hold the CYCLE. If so, only flag that motion cancel is complete.

// 动作取消只发生在一个周期中,但是,安全门可能事先被启动循环。如果是这样,只有运动标志取消已经完成。

if (sys.state == STATE_CYCLE) { sys.state = STATE_MOTION_CANCEL; }

sys.suspend |= SUSPEND_MOTION_CANCEL; // Indicate motion cancel when resuming. Special motion complete.

}

// Execute a feed hold with deceleration, only during cycle.

if (rt_exec & EXEC_FEED_HOLD) {

// Block SAFETY_DOOR state from prematurely changing back to HOLD.

if (bit_isfalse(sys.state,STATE_SAFETY_DOOR)) { sys.state = STATE_HOLD; }

}

// Execute a safety door stop with a feed hold, only during a cycle, and disable spindle/coolant.

// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered

// devices (spindle/coolant), and blocks resuming until switch is re-engaged. The power-down is

// executed here, if IDLE, or when the CYCLE completes via the EXEC_CYCLE_STOP flag.

if (rt_exec & EXEC_SAFETY_DOOR) {

report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);

// If already in active, ready-to-resume HOLD, set CYCLE_STOP flag to force de-energize.

// NOTE: Only temporarily sets the 'rt_exec' variable, not the volatile 'rt_exec_state' variable.

if (sys.suspend & SUSPEND_ENABLE_READY) { bit_true(rt_exec,EXEC_CYCLE_STOP); }

sys.suspend |= SUSPEND_ENERGIZE;

sys.state = STATE_SAFETY_DOOR;

}

}

bit_false_atomic(sys.rt_exec_state,(EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR));  //清除(执行取消动作)(执行进给保持)(执行安全门)标志位

}

// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.

// 执行一个循环开始启动步进开始执行中断队列的街区

if (rt_exec & EXEC_CYCLE_START) { //循环开始状态进入

// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.

// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.

//块如果在同时举行的命令:保持进给,运动取消,和安全的门。 //确保auto-cycle-start没有简历没有显式的用户输入。

if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {   //状态机如果不是保持进给,运动取消,和安全的门。

// Cycle start only when IDLE or when a hold is complete and ready to resume.

// NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.

//循环开始时只有当闲置或持有完成并准备简历。

//注意:SAFETY_DOOR是隐式地屏蔽。它返回的时候门是关闭的。

// 如果系统状态为闲着状态,系统状态为开始进给或运动取消,暂停标志为位重新开始

if ((sys.state == STATE_IDLE) || ((sys.state & (STATE_HOLD | STATE_MOTION_CANCEL)) && (sys.suspend & SUSPEND_ENABLE_READY))) {

// Re-energize powered components, if disabled by SAFETY_DOOR.

// 由SAFETY_DOOR重振组件供电,如果禁用。

if (sys.suspend & SUSPEND_ENERGIZE) {

// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.

//延迟任务:重新启动主轴和冷却剂,延迟升高,然后恢复周期。

if (gc_state.modal.spindle != SPINDLE_DISABLE) {   //主轴模式不是失能进入

spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed); //设置状态和速度

delay_ms(SAFETY_DOOR_SPINDLE_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.

}

if (gc_state.modal.coolant != COOLANT_DISABLE) {

coolant_set_state(gc_state.modal.coolant);

delay_ms(SAFETY_DOOR_COOLANT_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.

}

// TODO: Install return to pre-park position.

}

// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.

//开始循环只有排队运动存在于运动没有取消和绘图缓冲器。

//只有在队列马达存在规定的缓冲,并且动机没有让取消,才会循环

if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {

sys.state = STATE_CYCLE;

st_prep_buffer(); // Initialize step segment buffer before beginning cycle.初始化步开始循环之前

st_wake_up();

} else { // Otherwise, do nothing. Set and resume IDLE state.否则,什么也不做,设置和复位空闲模式

sys.state = STATE_IDLE;

}

sys.suspend = SUSPEND_DISABLE; // Break suspend state.

}

}

bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_START);

}

// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by

// realtime command execution in the main program, ensuring that the planner re-plans safely.

// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper

// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.

// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.

//重新启动后循环计划和步进系统的进给保持简历。通过实时命令执行主程序,确保安全计划重新计划。

//注意:画线算法变量仍保持通过规划师和步进

//循环仅。步进路径应该继续,好像什么都没发生一样。

//注意:EXEC_CYCLE_STOP由步进子系统周期或进给保持时完成。

if (rt_exec & EXEC_CYCLE_STOP) { //如果是循环停止状态进入

if (sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) {

// Hold complete. Set to indicate ready to resume.  Remain in HOLD or DOOR states until user

// has issued a resume command or reset.

//保存完整。设置为指示准备简历。继续持有或门状态,直到用户

//已发布了一份简历命令或重置。

if (sys.suspend & SUSPEND_ENERGIZE) { // De-energize system if safety door has been opened. 如果安全的门已被打开。断开系统

spindle_stop();

coolant_stop();

// TODO: Install parking motion here. 安装停车动作。

}

bit_true(sys.suspend,SUSPEND_ENABLE_READY);

} else { // Motion is complete. Includes CYCLE, HOMING, and MOTION_CANCEL states. 电机完成,循环,回原点,,MOTION_CANCEL

sys.suspend = SUSPEND_DISABLE;

sys.state = STATE_IDLE;

}

bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_STOP);

}

}

// Overrides flag byte (sys.override) and execution should be installed here, since they

// are realtime and require a direct and controlled interface to the main stepper program.

//重写标志字节(sys.override)和执行应该安装在这里,因为他们

//实时和需要直接和控制接口的主要步进程序。

// Reload step segment buffer 重新加载步段缓冲

if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR | STATE_HOMING)) { st_prep_buffer(); }

// If safety door was opened, actively check when safety door is closed and ready to resume.

// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.

//如果安全的门被打开,积极检查当安全门关闭,准备简历。

//注意:这解锁SAFETY_DOOR状态保持状态,这样CYCLE_START可以激活一个简历。

if (sys.state == STATE_SAFETY_DOOR) { //安全门状态进入

if (bit_istrue(sys.suspend,SUSPEND_ENABLE_READY)) {

if (!(system_check_safety_door_ajar())) {

sys.state = STATE_HOLD; // Update to HOLD state to indicate door is closed and ready to resume. 更新保存状态指示门关闭,准备简历。

}

}

}

} while(sys.suspend); // Check for system suspend state before exiting.

}

__________________________________________________________________________________________

//阻塞,直到所有缓冲的步骤执行或循环状态。与进给保持

//在一个同步调用,如果它会发生。同时,等待清洁周期结束。

void protocol_buffer_synchronize()

{

// If system is queued, ensure cycle resumes if the auto start flag is present.

//如果排队系统,确保循环简历如果汽车开始标记。

protocol_auto_cycle_start();

do {

protocol_execute_realtime();   // Check and execute run-time commands

if (sys.abort) { return; } // Check for system abort

} while (plan_get_current_block() || (sys.state == STATE_CYCLE));

}

分享:

grbl源代码protocal部分相关推荐

  1. 移植GRBL控制器到ESP32实现写字机器人

    1.前述   在学校的时候,有一次参加电赛选的电机夹笔的题目,用的步进电机和导轨,那会吧,步进电机用的少整的不太行,最后拿了个省三,磕碜人.其实现在想想,那个题目也不需要什么算法,就是细节没处理好,这 ...

  2. 基于stm32的grbl写字机器人(添加舵机支持)

    背景: 在移植grbl到stm32的时候,源代码在Z轴抬笔控制支持使用的是步进电机,但是我手头上只有一个9G舵机,并且网上售卖的步进电机体积偏大不宜安装在Z轴笔架上,所以需要阅读代码,在了解Z轴的控制 ...

  3. Grbl v1.1版本的编译方法

    Grblv1.1版本的编译方法wiki翻译 Grbl v1.1 compile source website: https://github.com/gnea/grbl/wiki/Compiling- ...

  4. 艰难的LinuxCNC(EMC2)源代码安装依赖01

    艰难的LinuxCNC(EMC2)源代码安装依赖01 为了写一个圆弧补偿的程序,分析了ode,想再深入了解一下,在linuxCNC(emc2)系统仿真,看看它的算法,没有想到源代码安装,有如此庞大的依 ...

  5. grbl1.1源代码分析

    GRBL 1.1源代码分析 感谢硬件测试提供者 https://shop298719590.taobao.com/ 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接 ...

  6. grbl控制3轴机械臂 原理 实现 (三) 之如何通过步进电机控制机械臂、插补算法

    参考: 图形学入门 https://www.zhihu.com/question/20330720 https://zhuanlan.zhihu.com/p/30553006 https://www. ...

  7. [计算机毕设]基于java的打飞机游戏系统设计与实现(项目报告+源代码)

    基于java的打飞机游戏系统项目说明报告 1.1 手机软件现状 在信息社会中,手机及其他无线设备越来越多的走进普通百姓的工作和生活,随着信息网络化的不断进展,手机及其他无线设备上网络势在必行.但是传统 ...

  8. Mac 下使用 VS Code 生成 C++ 的 Google Protocal Buffer 项目及使用记录

    Mac下Protobuf编译及使用记录 前言 信息传输格式,相比于xml和json的纯文本格式,protocal是经过序列化的,显然空间占用更小,但也导致了读取比较复杂. 正常读取需要protobuf ...

  9. 瑞波(ripple)提出的跨链技术 Interledger Protocal( ILP)详解

    特别声明: 本文具有较强原创性和先导性,在此只做笔记使用,严禁任何转载和引用, 随着区块链网络广泛的出现,在不同的网络之间实现加密电子货币的价值转换的需求变得旺盛,作为价值网络核心的跨链技术从而变得越 ...

最新文章

  1. 多伦多大学2020春季CSC311课程「机器学习导论」课件PPT
  2. php截取剩余部分,PHP从字串中截取一部分,支持使用(*)模糊截取
  3. 吃CPU的openmp 程序
  4. 动态规划(最长递增子序列)---最长递增子序列
  5. python IDLE中反斜杠显示为人民币符号¥的解决办法
  6. Kafka学习笔记(3)----Kafka的数据复制(Replica)与Failover
  7. Android系统启动系列----init进程
  8. java找到int需要char_Java2课后择题.doc
  9. 浅谈自执行函数-立即调用的函数表达式
  10. 大班科学计算机的发明应用教案,大班科学教案:机器人探密
  11. 【EMNLP2020】最后论文征稿通知和常见问题解答
  12. NVIDIA英伟达控制面板打不开解决办法win10
  13. cad命令栏还原默认_CAD命令行不见了怎么重新恢复?
  14. 国家企业信用信息查询工商数据爬虫
  15. struts2和hibernate的简单新闻发布系统_点赞!北斗卫星导航系统28nm工艺芯片已量产,全球范围定位精度优于10米...
  16. UltraCompare for Mac(文件内容对比神器)v22
  17. 港科夜闻|香港科大彭倩教授分析疫情下企业如何减低投资损失
  18. G711音频编码格式
  19. Java算法完美解决五位哲学家用餐问题
  20. 计算数学学者网站推荐

热门文章

  1. 引流软件哪个好,脚本引流软件实战解说
  2. Win11系统声音怎么设置?Win11怎么调声音?
  3. 《大话脑成像》之浅谈功能脑网络
  4. ArcGIS二次开发之一:在ArcMap中启动ArcCatalog的代码实现
  5. java编辑2048小游戏_Java 制作命令行版 2048小游戏
  6. 物联网技术如何推进乡村振兴
  7. 如何将两张图片合成一张pdf
  8. Uboot9之uboot目录分析
  9. 谭浩强《C++面向对象程序设计》知识点总结
  10. 数字电子技术实验ICC作业——引讲视频(攻略)