出于一些原因我搞到了一台 Ender-3 V2 打印机，拧好了螺丝调好了平之后开机发现它读不出自带的 TF 卡里的文件。于是从树莓派里掏张卡出来，装几个 gcode 文件进去，重启打印机⸺然后发现卡在启动界面了。查打印机的说明书才发现它在开机时会自动读取 TF 卡里 .bin 文件。原来用在树莓派上的卡里肯定有不少 .bin 文件，猜测是打印机把其中的一个当打印机固件读取并刷写了，导致不开机。那怎么修复呢？你想看普通的修复教程？不不，那样就和随处可见（存疑）的论坛帖子没有区别了，这是为了博客特制的，明白吗？请阅读下文以感受我修复之路的痛楚⋯⋯尝试“正常”的修复方法去 创想三维的固件发布页 找到了原版固件，阅读说明得知只要把新固件放进 TF 卡里装进机器就可以自动更新固件。听起来非常的简单是不是，我们动手做一下吧。先把树莓派卡格式化成 FAT32，装上固件⸺等一下，这么多固件我选哪个，怎么也没有说明？从主板盖板下面的缝里打光能看到主板版本是 V4.2.2，就选个比较新而且不花里胡哨的 Ender-3 V2_HW_V4.2.2_SW_V1.0.7 吧。装上 TF 卡，重启打印机，然而并没有解决问题，还是卡在启动画面。换了原装 TF 卡装固件，还是没有作用。上网查一下呢？在 一篇红迪帖子 里找到了修复方法：用一张格式化为 FAT32，簇大小 4096B，容量不超过 32GB 的 TF 卡装上固件插进打印机里。按照指示重新格式化了卡然后装上固件，但依旧没有解决问题。打开主板盖板，拔掉常开风扇、挤出头加热棒、挤出头热敏电阻的线。还是没用。拆开挤出头，松一松固定热敏电阻的螺丝。什么也没有发生。那接下来怎么办？开始发癫抱着试一试的想法插了根线到打印机的 USB 口，然后 lsusb 一下，发现了经典 CH340 串口芯片。用串口终端打开一看，还真收到了打印机发送的信息：Update failed, No SD/TF-Card or error。但我明明装了卡啊，而且一开始不就是因为从这卡上错误地读了二进制文件才导致了问题的吗？猜测是误刷的固件把整块主控都搞得乱糟糟的了（实际上这个猜测错得离谱)｡在主板上一通搜索，发现了熟悉的 SWDIO 和 SWCLK SWD 接口。连上 DAPLink 调试器，pyOCD 启动！警告：不要贸然尝试以下的任何代码！SWD 刷写很容易就可以把你的主控送上天！初步尝试 SWD观察主板得知主控芯片是 GD32F303RET6，那就先下载对应的目标文件：pyocd pack install GD32F303RE，成功建立与主控的通信。以防万一先读取备份一下主控的固件：pyocd cmd --target gd32f303re -c reset halt -c savemem 0 0x400000 e3v2_gd32f303_dump.bin（事后发现这个操作救了我）现在我们可以大胆去做了。先 pyocd erase -t gd32f303re -c 清空存储器，然后 pyocd flash --target gd32f303re -v ./GD-Ender-3\ V2Marlin2.0.6.1V1.0.5HW4.2.2 试着刷写原厂固件。然后不出所料地没用。插上 USB 线发现糟了，串口都没输出了。再次上网查资料：神秘的 RepRap 论坛 网友提到 V4.2.2 版本主板的固件应该存在 0x08007000 位置之后，而 0x0800000-0x08006FF 的这一段地址是分配给 bootloader 的。继续查阅 GD32F303 的技术手册，发现 0x00000000-0x002fffff 的这一段地址是映射到位于 0x08000000 - 0x082fffff 的主 FLASH 存储器的，而默认状况下这颗芯片上电后从 0x00000000 开始执行。看来通过 SWD 直写固件不工作的原因是固件需要依赖 bootloader 的存在才能工作，或者是链接时写死的内存位置因为我从 0x0000000 开始直接写入固件也混乱了。那么，原本的 bootloader 似乎不起作用，厂商提供的固件又依赖 bootloader 才能工作，我该怎么办呢？似乎自己编译固件才是唯一的方法了，毕竟这只是个普通的微控制器，也有开源的固件，想整一些非常规的花活应该也不困难。编译固件还好 Ender-3 V2 原装的固件就是基于 Marlin 的，对它的社区支持也相当完善，按照 Marlin 官方指引 很快就改好了一个配置文件。这里要注意创想三维在不改变主板版本号的情况下居然推出了使用两种（甚至更多）主控芯片的主板：有的是 STM32F1 系列，有的是 STM32F3 系列，还有的是 GD32F3 系列，而且（显然的）它们的固件并不通用，这也是为什么在原厂固件下载页面会有带 GD- 前缀和不带的固件版本。真不愧是三轮厂，我无语了。因为我的主板使用 GD32F303，所以要注意在 Configuration.h 里设置 #define MOTHERBOARD BOARD_CREALITY_V422_GD32_MFL。改配置文件本身倒没什么难的，直接复制官方给的配置例子都没什么问题。只是注意如果使用非 CrealityUI 的话，对屏幕的版本也有要求（对，创想三维还搞出了不同屏幕的花活)｡然后在 ini/gd32.ini 里修改board_build.offset = 0x7000 board_upload.offset_address = 0x08007000为board_build.offset = 0x0 board_upload.offset_address = 0x08000000然后重新编译固件。现在通过 SWD 直写到 0x0 总能运行了吧？⋯⋯并不能。到底哪出错了？可能是固件的代码并不完全依赖这两个值计算地址，或许在什么地方有控制链接器行为的代码，但要调整那些的话难度也会陡然上升，再折腾下去时间成本就太高了，不如买块新主板了事。从头再来睡了一觉，倔劲又上来了，决定从头再来：看看一开始读取出来的固件，验证一下我之前的猜想。xxd e3v2_gd32f303_dump.bin | less 发现在 0x7000 之前的内容看起来确是 bootloader，且没有用完这段空间，在 0x6100 之后就全都是 0xFF 了，这样就和之前发现的地址布局相符。搜索一下，甚至能在 bootloader 的二进制文件里找到之前的 "Update failed, No SD/TF-Card or error" 字符串。继续往后看，0x7000 之后的空间里居然出现了和树莓派相关的字符串，证明打印机是错误读取了树莓派卡里的二进制文件才导致问题的猜测也是正确的。先刷回原来的 bootloader 试试。如果用 SWD 刷回的 bootloader 能产生串口输出，那我至少没把主控搞坏。用 dd if=ender3fwbk.bin of=extracted_bootloader.bin bs=1 count=$((0x7000)) 提取出二进制固件的前 0x7000 字节即为 bootloader。pyocd erase -t gd32f303re -c 把主控清除。接着 pyocd flash --target gd32f303re -v -a 0x08000000 extracted_bootloader.bin 把提取出的 bootloader 写入到 0x08000000。开机发现串口重新有输出了！继续试验：用回原来的 *.offset 值编译固件，然后 pyocd flash --target gd32f303re -v -a 0x08007000 刷写到 bootloader 之后的预定位置。开机，居然成功解决了问题？难道原来的 bootloader 没有损坏，之后的固件依然能读取？本来想在 0x08007000 之后刷回原厂固件测试一下的，但发现不知为何 SWD 已经连接不上主控了（可能是固件重设了 SWD 的映射引脚），但反正现在的固件也能用，遂作罢。总结清空树莓派 TF 卡，装上几个 GCode 文件插进打印机，发现不识别了。这时候想起来之前查资料的时候看到过 TF 卡的格式要求除了 FAT32 文件系统，4096B 簇大小以外还要求 MBR 分区表，大小不超过 8GB。按照这个要求重新格式化 TF 卡并装上 Gcode 文件，居然能读出来了。现在回想起来，我一开始假设 bootloader 被写坏或许就是错的。bootloader 并没有坏，是我的卡没有按要求格式化，导致装上原厂固件后插进机器不识别，才有了后面兜的大圈子。归根结底是创想三维不明确写明对 TF 卡的格式要求才导致了这一问题的发生，而且受害者远远不止我一个。珍爱生命，远离三轮厂⋯⋯附录：bootloader 文件如果有倒霉蛋不幸搞丢了自己的 bootloader，在此提供一份我提取的：extracted_firmware.bin附录：如何在 GNU/Linux 上按要求格式化 TF 卡插上新的装有 Gcode 的 TF 卡发现还是读不出来，只好重新格式化一下：lsblk 找到卡的块设备名。按照 SIZE 应该很好认出来。比如我的卡就显示为：sdb 8:16 1 29G 0 disk └─sdb1 8:17 1 5.6G 0 part在下文我会以此为例给出命令，请根据自己的系统调整。请再三确认你输对了目标，不然接下来的操作就要让你宝贵的数据进火葬场了。sudo wipefs -a /dev/sdb 清空卡上的所有文件系统。sudo parted /dev/sdb --script mklabel msdos 创建一个 DOS (MBR) 分区表，然后 sudo parted /dev/sdb --script mkpart primary fat32 1MiB 100% 创建一个使用整张卡容量的 FAT32 分区。有人提到过打印机不认大于 8G 的卡，我倒是没有遇到，但你或许可以试试调小分区以防患于未然。sudo mkfs.vfat -F 32 0s 8 -S 512 -n SDCARD /dev/sdb1 把新创建的分区设置到 512B 每扇区 × 8 扇区 = 4096B 簇大小｡(题外话：“簇”是什么垃圾翻译？计算机领域的名词怎么都翻译得这么懒？诸位读者以后请务必用心翻译专业名词谢谢喵)你说你用的是 Windows？那不关我的事喵。
用Arduino制作一个摇杆式鼠标&温度监视器
为了把之前挖的说要搭一台UMPC的大坑给填上，必须要有一个足够小的、能当鼠标用的装置，所以就弄出了这么个玩意...首先看看这个项目的原型，Toshiba Libretto系列和Thinkpad 750，都是使用的指点杆然而指点杆太贵了，一个要40多，而且PCB还很大，装不进键盘里，所以实际上和摇杆没什么区别了...那就弄摇杆好了...然后事实证明便宜确实没好货，摇杆转20°左右就满量程了，而且阻尼很大，用起来让人觉得喝了假酒那么要怎么让它用起来稍微好一点呢？因为阻尼大，转起来很难精确控制转动的角度 --> 那就降低光标的移动速度然而这样光标要走过很长的距离时就很痛苦 --> 弄个加速键这样虽然说妥协很大，用起来远远不如小红点顺手，但是...嘿！它能用！再把左键，右键，中键加上之后用一段时间还算顺手，那就这样了吧...既然用了Arduino来当鼠标了，那不加一块屏幕就太浪费了于是就弄了那块128*64，0.96'的OLED屏幕显示下温度，风扇转速和警告信息，看起来相当不错(最终效果见上图)(测试中...)然后是用这玩意解决一些在测试其它系统的时候发现的问题：本来是准备用一个简单的霍尔+MOSFET的独立电路来做到控制主屏幕在开合时的开关的，结果P沟道MOSFET忘买了...再去为了一个MOSFET付十块钱的运费实在是不值，也懒得拿三极管做非门了...那就干脆用Arduino解决算了这里没什么好讲的，就是给栅极10k的下拉电阻似乎太小了，直接上680k下拉解决键盘是买的现成的，拿到手发现虽然质量还行，但是有Z+Shift会和方向左键冲突...本来这不是什么大问题，但是对于东方玩家来说实在致命...于是就弄了个自锁开关，按下会一直按Z，问题解决！最后，还有50%的剩余ROM空间，那就加个非常中二的Boot Screen好了...最后的最后，放下写的稀烂的代码(BootScreen删掉了)#include <Mouse.h> #include <Keyboard.h> #include <U8glib.h> U8GLIB_SSD1306_128X64 u8g(4, 5, 6); const int xPin = A0; const int yPin = A1; const int caliButton = 9; const int caliIndi = 16; const int lButton = 12; const int rButton = 10; const int mainSW = 3; const int scrlSW = 11; const int accSW = 2; const int zButtonSW = 1; const int CPUTempSensor = A5; const int battTempSensor = A4; const int fanPin = 0; const int hallSensor = 7; const int screenMOSFET = 8; //Pin configuration int xCali; int yCali; //Create variables for calibration int xCalied; int yCalied; int xProcessed; int yProcessed; int CPUtemp; int battTemp; int fanSpeed; boolean mouseSwitch; //Declaring variables void setup() { Serial.begin(9600); Mouse.begin(); mouseSwitch = 0; pinMode(accSW, INPUT_PULLUP); u8g.setFont(u8g_font_7x13B); } void loop() { CPUtemp = ((analogRead(CPUTempSensor) - 240) / 11); battTemp = ((analogRead(battTempSensor) - 240) / 11);//Temperature monitoring fanSpeed = (CPUtemp * 6) - 240; if (fanSpeed > 0){ analogWrite(fanPin, fanSpeed); }else{ analogWrite(fanPin, 0); }//Fan control if (digitalRead(hallSensor) == HIGH){ digitalWrite(screenMOSFET, HIGH); }else{ digitalWrite(screenMOSFET, LOW); } int xRaw = analogRead(xPin) - 512; int yRaw = analogRead(yPin) - 512; if (digitalRead(caliButton) == HIGH){ xCali = 0 - xRaw; yCali = 0 - yRaw; digitalWrite(caliIndi, HIGH); }else{ digitalWrite(caliIndi, LOW); } xCalied = (xRaw + xCali)/4; yCalied = (yRaw + yCali)/4; if (digitalRead(accSW) == LOW){ xProcessed = ((10 * cos(0.01 * xCalied)) - 10 + xCalied) / 10; yProcessed = ((10 * cos(0.01 * yCalied)) - 10 + yCalied) / 10; }else{ xProcessed = ((10 * cos(0.01 * xCalied)) - 10 + xCalied) / 16; yProcessed = ((10 * cos(0.01 * yCalied)) - 10 + yCalied) / 16; } if (digitalRead(mainSW) ==HIGH){ mouseSwitch = 1; }else{ mouseSwitch = 0; } if (mouseSwitch ==1){ if (digitalRead(lButton) == HIGH){ Mouse.press(MOUSE_LEFT); }else{ Mouse.release(MOUSE_LEFT); } if (!(digitalRead(rButton) == HIGH && digitalRead(scrlSW) == HIGH)){ if (digitalRead(rButton) == HIGH){ Mouse.press(MOUSE_RIGHT); }else{ Mouse.release(MOUSE_RIGHT); } if (digitalRead(scrlSW) == HIGH){ Mouse.move(0, 0, xProcessed / 4); }else{ Mouse.move(yProcessed, -xProcessed); } Mouse.release(MOUSE_MIDDLE); }else{ Mouse.press(MOUSE_MIDDLE); Mouse.move(yProcessed, -xProcessed); } } if (digitalRead(zButtonSW) == HIGH){ Keyboard.press('z'); }else{ Keyboard.release('z'); } u8g.firstPage(); do{ u8g.setFont(u8g_font_7x13B); u8g.drawFrame(2, 2, 126, 62); u8g.drawStr(4, 13, "CPU Temp:"); u8g.setPrintPos(70, 12); u8g.print(CPUtemp); u8g.drawStr(4, 27, "Batt Temp:"); u8g.setPrintPos(77, 27); u8g.print(battTemp); u8g.drawStr(105, 62, "RPM"); u8g.drawLine(105, 50, 126, 50); u8g.drawLine(105, 6, 126, 6); if (fanSpeed > 0){ u8g.drawLine(108, (-(fanSpeed / 6) + 50), 123, (-(fanSpeed / 6) + 50)); } u8g.drawStr(22, 48, "[ ][ ][ ]"); if (battTemp > 50){ u8g.drawStr(29, 48, "!"); } if (digitalRead(caliButton) == HIGH){ u8g.drawStr(50, 48, "*"); } if (digitalRead(zButtonSW) == HIGH){ u8g.drawStr(73, 48, "Z"); } } while(u8g.nextPage()); }By Zephyr2552020/8/21