Arduino UNO Logic Sniffer: 8 ขั้นตอน (พร้อมรูปภาพ)
Arduino UNO Logic Sniffer: 8 ขั้นตอน (พร้อมรูปภาพ)
Anonim
Arduino UNO Logic Sniffer
Arduino UNO Logic Sniffer

โครงการนี้เริ่มต้นจากการทดลองง่ายๆ ระหว่างการวิจัยเอกสารข้อมูลของ ATMEGA328P สำหรับโครงการอื่น ฉันพบสิ่งที่น่าสนใจมากกว่า หน่วยจับสัญญาณเข้า Timer1 ช่วยให้ไมโครคอนโทรลเลอร์ของ Arduino UNO ตรวจจับขอบของสัญญาณ จัดเก็บการประทับเวลา และทริกเกอร์การขัดจังหวะ ทั้งหมดในฮาร์ดแวร์

จากนั้นฉันก็สงสัยว่าแอปพลิเคชันใดจะมีประโยชน์ และจะทดสอบอย่างไร เนื่องจากฉันต้องการตัววิเคราะห์ลอจิกมาระยะหนึ่งแล้ว ฉันจึงตัดสินใจลองใช้หนึ่งในบอร์ด Arduino UNO ของฉัน เพื่อทดสอบฟีเจอร์นี้ และดูว่าเราจะได้ผลลัพธ์ที่ดีหรือไม่

ฉันไม่ใช่คนเดียวที่มีแนวคิดนี้ และคุณจะพบกับพวกเขามากมายเพียงแค่กูเกิล "Arduino Logic Analyzer" ในตอนเริ่มต้นของโปรเจ็กต์ เนื่องจากเพิ่งเริ่มต้นจากการทดลอง ฉันไม่รู้ด้วยซ้ำว่ามีคนสร้างมันขึ้นมาแล้ว และรู้สึกประทับใจกับผลลัพธ์ที่ดีที่พวกเขาได้รับจากฮาร์ดแวร์ชิ้นเล็กๆ ชิ้นนี้ อย่างไรก็ตาม ฉันไม่พบโปรเจ็กต์อื่นที่ใช้อินพุตแคปเจอร์ยูนิต ดังนั้นหากคุณเคยเห็นสิ่งนี้แล้ว โปรดแจ้งให้เราทราบ!

เพื่อสรุป ตัววิเคราะห์ลอจิกของฉันจะ:

  • มีช่องเดียว
  • มีส่วนต่อประสานกราฟิก
  • สื่อสารกับอินเทอร์เฟซผ่าน USB,
  • ทำงานบนบอร์ด Arduino UNO

ในที่สุดมันก็จะมีความลึกของหน่วยความจำ 800 ตัวอย่างและสามารถจับภาพข้อความ UART 115200 bauds ได้สำเร็จ (ฉันไม่ได้ทดสอบด้วยความเร็วสูงจริงๆ)

คำแนะนำนี้มีทั้งส่วน "วิธีการทำงาน" และ "วิธีใช้งาน" ของโครงการนี้ ดังนั้นสำหรับผู้ที่ไม่สนใจด้านเทคนิค คุณสามารถข้ามไปยังขั้นตอนที่ 4 ได้โดยตรง

เสบียง

ฉันต้องการทำให้เครื่องวิเคราะห์เรียบง่ายที่สุดเท่าที่จะเป็นไปได้ ดังนั้นจึงต้องใช้ฮาร์ดแวร์เพียงเล็กน้อย

คุณจะต้องการ:

  • บอร์ด Arduino UNO (หรือเทียบเท่า ตราบใดที่ใช้ ATMEGA328P MCU)
  • คอมพิวเตอร์,
  • สิ่งที่ต้องแก้ไขข้อบกพร่อง (บอร์ด Arduino UNO ตัวอื่นทำงานได้ดีในการทดสอบบางอย่าง)

สามารถดูโค้ดสำหรับทั้ง Arduino UNO และเว็บอินเตอร์เฟสได้ที่นี่ คุณจะต้องใช้ซอฟต์แวร์ p5.serialcontrol และ PulseView

ขั้นตอนที่ 1: หลักการทำงาน

หลักการทำงาน
หลักการทำงาน

ความคิดนั้นง่าย คุณเลือกการตั้งค่าการจับภาพ และคลิกที่ "รับ" เว็บอินเทอร์เฟซจะส่งไปยังซอฟต์แวร์ p5.serialcontrol ซึ่งช่วยให้เราใช้อินเทอร์เฟซแบบอนุกรมจากเบราว์เซอร์ได้ เนื่องจากไม่สามารถเข้าถึงได้โดยตรง จากนั้นซอฟต์แวร์ p5.serialcontrol จะส่งข้อมูลไปยังบอร์ด Arduino UNO ซึ่งจะเก็บข้อมูลและส่งกลับไปยังอินเทอร์เฟซผ่านเส้นทางเดียวกัน

ง่าย! อืม… เนื่องจากฉันไม่เก่งเรื่องการเขียนโปรแกรมส่วนต่อประสานระหว่างมนุษย์/เครื่องจักรหรือเทคโนโลยีเว็บ ของฉันจึงค่อนข้างน่าเกลียดและบั๊กกี้ แต่มันช่วยให้ฉันสามารถเริ่มการจับภาพและดึงข้อมูลของฉันกลับมาได้ ซึ่งเป็นสิ่งที่มันถูกออกแบบมาสำหรับ ดังนั้นฉันคิดว่ามันใช้ได้ สำหรับงานวิเคราะห์ที่จริงจังยิ่งขึ้น ฉันนำเข้าบันทึกของฉันไปยัง PulseView ซึ่งใช้งานง่ายและมีชุดคุณสมบัติและตัวถอดรหัสโปรโตคอลที่ดี ตามที่เราจะเห็นในภายหลัง

หน่วยจับสัญญาณอินพุตของ Arduino UNO สามารถกำหนดค่าให้ใช้การแบ่งสัญญาณนาฬิกาที่แตกต่างกัน ซึ่งจะช่วยลดความละเอียดได้ แต่จะเพิ่มการหน่วงเวลาก่อนที่จะล้น นอกจากนี้ยังสามารถทริกเกอร์การขึ้น ลง หรือทั้งสองข้างเพื่อเริ่มบันทึกข้อมูล

ขั้นตอนที่ 2: Arduino UNO Sketch

Arduino UNO Sketch
Arduino UNO Sketch

ฉันเขียนและรวบรวมภาพร่างด้วย Arduino IDE ครั้งแรกที่ฉันเริ่มต้นด้วยการตั้งค่า Timer1 ในโหมดการทำงาน "ปกติ" โดยเขียนไปยังการลงทะเบียน TCCR1A และ TCCR1B ในการตั้งค่า () จากนั้นฉันก็สร้างฟังก์ชันบางอย่างเพื่ออำนวยความสะดวกในการใช้งานอีกเล็กน้อยในอนาคต เหมือนกับฟังก์ชันสำหรับตั้งค่าการแบ่งนาฬิกาที่ชื่อว่า "setTim1PSC()" ฉันยังเขียนฟังก์ชันเพื่อเปิดใช้งานและปิดใช้งานหน่วยจับอินพุต Timer1 และการขัดจังหวะโอเวอร์โฟลว์

ฉันเพิ่มอาร์เรย์ "ตัวอย่าง" ซึ่งจะเก็บข้อมูลที่ได้รับ เป็นอาร์เรย์สากลที่ฉันตั้งค่าเป็น "ระเหย" เพื่อป้องกันไม่ให้คอมไพเลอร์ทำการเพิ่มประสิทธิภาพและวางไว้ในแฟลช เช่นเดียวกับที่ทำระหว่างการรวบรวมครั้งแรกของฉัน ฉันกำหนดให้มันเป็นอาร์เรย์ "uint16_t" เนื่องจาก Timer1 เป็น 16 บิตด้วยความยาว 810 เราหยุดการบันทึกที่ 800 ค่า แต่เนื่องจากการทดสอบเสร็จสิ้นนอกการขัดจังหวะด้วยเหตุผลด้านความเร็วที่ชัดเจน ฉันจึงเลือกที่จะเก็บ 10 ค่ามากขึ้นเพื่อป้องกันการล้น ด้วยตัวแปรเพิ่มเติมสองสามตัวสำหรับโค้ดที่เหลือ ภาพสเก็ตช์นี้ใช้หน่วยความจำ 1313 ไบต์ (88%) ทำให้เรามี RAM ว่าง 235 ไบต์ เราใช้หน่วยความจำสูงอยู่แล้ว และฉันไม่ต้องการเพิ่มความจุตัวอย่างมากขึ้น เนื่องจากอาจทำให้เกิดพฤติกรรมแปลก ๆ เนื่องจากมีพื้นที่หน่วยความจำน้อยเกินไป

ในภารกิจของฉันที่จะเพิ่มความเร็วในการดำเนินการอยู่เสมอ ฉันใช้ตัวชี้ฟังก์ชันแทนการใช้คำสั่ง if ภายในอินเตอร์รัปต์ เพื่อลดเวลาในการดำเนินการให้เหลือน้อยที่สุด หมุดจับจะเป็น Arduino UNO หมายเลข 8 เสมอ เนื่องจากเป็นหมุดเดียวที่เชื่อมต่อกับหน่วยจับสัญญาณอินพุตของ Timer1

ขั้นตอนการจับภาพจะแสดงอยู่ในภาพด้านบน เริ่มต้นเมื่อ Arduino UNO ได้รับกรอบข้อมูล UART ที่ถูกต้อง ซึ่งมีการตั้งค่าการจับภาพที่ต้องการ จากนั้นเราประมวลผลการตั้งค่าเหล่านั้นโดยกำหนดค่ารีจิสเตอร์ที่ถูกต้องเพื่อจับภาพบนขอบที่เลือก และใช้การแบ่งนาฬิกาที่เหมาะสม จากนั้นเราเปิดใช้งานการขัดจังหวะ PCINT0 (การเปลี่ยนพิน) เพื่อตรวจจับขอบสัญญาณแรก เมื่อเราได้รับ เราจะรีเซ็ตค่า Timer1 ปิดใช้งานการขัดจังหวะ PCINT0 และเปิดใช้งานการขัดจังหวะของ ICU (Input Capture Unit) จากช่วงเวลานั้นขอบที่ลดลง/เพิ่มขึ้นใดๆ ของสัญญาณ (ขึ้นอยู่กับการกำหนดค่าที่เลือก) จะทริกเกอร์ยูนิตดักจับอินพุต ซึ่งจะช่วยประหยัดเวลาของเหตุการณ์นี้ลงในรีจิสเตอร์ ICR1 และดำเนินการอินเตอร์รัปต์ ในอินเตอร์รัปต์นี้ เราใส่ค่ารีจิสเตอร์ ICR1 ลงในอาร์เรย์ "ตัวอย่าง" ของเรา และเพิ่มดัชนีสำหรับการจับภาพครั้งต่อไป เมื่อ Timer1 หรืออาร์เรย์โอเวอร์โฟลว์ เราจะปิดใช้งานการขัดจังหวะการดักจับ และส่งข้อมูลกลับไปยังอินเทอร์เฟซเว็บผ่าน UART

ฉันตัดสินใจใช้การขัดจังหวะการเปลี่ยนพินเพื่อทริกเกอร์การจับภาพ เนื่องจากยูนิตจับอินพุตอนุญาตให้จับภาพที่ขอบด้านใดด้านหนึ่งเท่านั้น ไม่ใช่ทั้งสองอย่าง นอกจากนี้ยังทำให้เกิดปัญหาเมื่อคุณต้องการจับภาพทั้งสองขอบ วิธีแก้ปัญหาของฉันคือการกลับบิตที่ควบคุมการเลือกขอบในการลงทะเบียนการควบคุมการดักจับอินพุตในแต่ละตัวอย่างที่ดึงมา วิธีนี้ทำให้เราสูญเสียความเร็วในการดำเนินการ แต่เรายังคงสามารถใช้ฟังก์ชันการทำงานของหน่วยดักจับอินพุตได้

ดังที่คุณอาจสังเกตเห็น เราไม่ได้จับตัวอย่างแต่ละตัวอย่างในช่วงเวลาที่แน่นอน แต่เราบันทึกช่วงเวลาที่เกิดการเปลี่ยนผ่านของสัญญาณ หากเราจับตัวอย่างหนึ่งตัวอย่างในแต่ละรอบสัญญาณนาฬิกา แม้ว่าจะมีการแบ่งนาฬิกาสูงสุด เราจะเติมบัฟเฟอร์ในเวลาประมาณ 0.1 วินาที สมมติว่าเราใช้ประเภท uint8_t ซึ่งเป็นตัวที่เล็กที่สุดในหน่วยความจำโดยไม่ต้องใช้โครงสร้าง

ขั้นตอนที่ 3: เว็บอินเตอร์เฟสและ P5.js

เว็บอินเตอร์เฟสและ P5.js
เว็บอินเตอร์เฟสและ P5.js

ตามความหมายของชื่อ เว็บอินเตอร์เฟสถูกสร้างขึ้นด้วยความช่วยเหลือของ p5.js ส่วนใครที่ยังไม่รู้ แนะนำให้ไปเช็คที่เว็บเลยครับ เพราะเป็นห้องสมุดที่ดีจริงๆ มันขึ้นอยู่กับการประมวลผล ใช้งานง่าย ช่วยให้คุณได้ผลลัพธ์ที่ดีอย่างรวดเร็ว และได้รับการบันทึกไว้อย่างดี ด้วยเหตุผลทั้งหมดที่ฉันเลือกห้องสมุดนี้ ฉันยังใช้ไลบรารี quicksettings.js สำหรับเมนู ตัว grafica.js เพื่อพล็อตข้อมูลของฉัน และไลบรารี p5.serialport เพื่อสื่อสารกับ Arduino UNO

ฉันจะไม่ใช้เวลามากเกินไปกับอินเทอร์เฟซ เนื่องจากฉันเพิ่งออกแบบสำหรับการแสดงตัวอย่างข้อมูลและการควบคุมการตั้งค่า และเนื่องจากมันไม่ได้เป็นหัวข้อในการทดสอบของฉันเลย อย่างไรก็ตาม ฉันจะอธิบายในส่วนต่างๆ ต่อไปนี้เกี่ยวกับขั้นตอนต่างๆ ในการใช้ทั้งระบบ ซึ่งจะอธิบายการควบคุมต่างๆ ที่มีอยู่

ขั้นตอนที่ 4: ตั้งค่าระบบ

สิ่งแรกคือต้องดาวน์โหลด Arduino UNO และโค้ดอินเทอร์เฟซที่นี่ หากยังไม่ได้ดำเนินการ จากนั้นคุณสามารถตั้งโปรแกรมบอร์ด Arduino UNO ใหม่ด้วยภาพร่าง "UNO_LS.ino" ผ่าน Arduino IDE

คุณควรดาวน์โหลดซอฟต์แวร์ p5.serialcontrol จากที่เก็บ github คุณต้องได้รับไฟล์ zip ที่ตรงกับระบบปฏิบัติการของคุณ (ฉันทดสอบบน Windows เท่านั้น) แตกไฟล์ zip ในโฟลเดอร์ เริ่มไฟล์ปฏิบัติการที่พบในนั้น และปล่อยไว้อย่างนั้น อย่าพยายามเชื่อมต่อกับพอร์ตอนุกรมใด ๆ เพียงแค่ปล่อยให้มันทำงานในพื้นหลัง มันจะใช้เป็นรีเลย์

เปิดโฟลเดอร์ "อินเทอร์เฟซ" คุณควรหาไฟล์ชื่อ "index.html" เปิดในเบราว์เซอร์ของคุณ มันคือเว็บอินเตอร์เฟส

และนั่นแหล่ะ! คุณไม่จำเป็นต้องดาวน์โหลดไลบรารี่เพิ่มเติม ทุกอย่างควรรวมอยู่ในแพ็คเกจที่ฉันให้ไว้

ขั้นตอนที่ 5: การเชื่อมต่อ การกำหนดค่า และการได้มา

การเชื่อมต่อ การกำหนดค่า และการได้มา
การเชื่อมต่อ การกำหนดค่า และการได้มา

หากต้องการเชื่อมต่ออินเทอร์เฟซกับบอร์ด Arduino UNO เพียงเลือกพอร์ตที่เกี่ยวข้องในรายการแล้วกดปุ่ม "เปิด" หากการดำเนินการสำเร็จ ข้อความ "สถานะ" ควรแสดงบางอย่างเช่น "เปิด COMX"

ตอนนี้คุณเลือกตัวเลือกการจับภาพได้แล้ว อันดับแรกคือการเลือกขอบ ฉันแนะนำให้คุณใช้ "ทั้งสอง" เสมอ เนื่องจากจะให้การแสดงสัญญาณที่แท้จริงได้ดีที่สุด หากการตั้งค่า "ทั้งสอง" ไม่สามารถจับสัญญาณได้ (เช่น หากความถี่ของสัญญาณสูงเกินไป) คุณสามารถลองใช้การตั้งค่าขอบ "เพิ่มขึ้น" หรือ "ลดลง" ขึ้นอยู่กับสัญญาณที่คุณพยายามจะมองเห็น

การตั้งค่าที่สองคือแผนกนาฬิกา มันจะให้ความละเอียดที่คุณจะสามารถจับสัญญาณได้ คุณสามารถเลือกตั้งค่าตัวประกอบการหารด้วย "8", "64", "256" และ "1024" บอร์ด Arduino UNO ใช้ควอตซ์ 16MHz เพื่อนาฬิกาไมโครคอนโทรลเลอร์ ดังนั้นความถี่สุ่มตัวอย่างจะเป็น "16MHz/ปัจจัยการแบ่ง" โปรดใช้ความระมัดระวังกับการตั้งค่านี้ เนื่องจากจะเป็นตัวกำหนดว่าคุณจะสามารถจับสัญญาณได้นานแค่ไหน เนื่องจาก Timer1 เป็นตัวจับเวลาแบบ 16 บิต เวลาในการจับภาพก่อนโอเวอร์โฟลว์จะเป็น "(2^16)*(division factor)/16MHz" ขึ้นอยู่กับการตั้งค่าที่คุณเลือก จะอยู่ในช่วงระหว่าง ~33ms ถึง 4.2s จำตัวเลือกของคุณไว้ในใจ คุณจะต้องใช้ในภายหลัง

การตั้งค่าสุดท้ายคือตัวตัดเสียงรบกวน ฉันไม่ได้ทำการทดสอบมากมาย และคุณไม่จำเป็นต้องใช้มันใน 99% ของกรณีทั้งหมด ดังนั้นอย่าเลือกมันเลย สำหรับผู้ที่ยังสงสัยเกี่ยวกับมัน คุณสามารถค้นหาตัวตัดเสียงรบกวนได้ในส่วน Timer/Counter1 ของแผ่นข้อมูลของ ATMEGA328P

อย่าลืมเชื่อมต่อพิน 8 ของบอร์ด Arduino UNO กับสัญญาณของคุณ และต่อสายดินเข้าด้วยกันเพื่อให้มีการอ้างอิงแรงดันไฟฟ้าเดียวกันสำหรับทั้งวงจรทดสอบและตัววิเคราะห์ลอจิก หากคุณต้องการการแยกกราวด์หรือต้องการวัดสัญญาณที่มีระดับต่างจาก 5V คุณอาจต้องเพิ่ม opto-isolator ให้กับวงจรของคุณ

เมื่อทุกอย่างได้รับการกำหนดค่าอย่างถูกต้องแล้ว คุณสามารถกดปุ่ม "รับ"

ขั้นตอนที่ 6: จับผลลัพธ์และส่งออกข้อมูล CSV

จับผลลัพธ์และส่งออกข้อมูล CSV
จับผลลัพธ์และส่งออกข้อมูล CSV

เมื่อ Arduino UNO ของคุณจับภาพเสร็จแล้ว มันจะส่งข้อมูลกลับไปยังเว็บอินเตอร์เฟสโดยอัตโนมัติ ซึ่งจะทำการลงจุด คุณสามารถซูมเข้าหรือซูมออกด้วยแถบเลื่อนด้านขวา และเลื่อนผ่านตัวอย่างด้วยแถบด้านล่าง

โครงเรื่องให้ภาพตัวอย่างเท่านั้น และไม่มีเครื่องมือวิเคราะห์ข้อมูล ดังนั้น เพื่อที่จะทำการวิเคราะห์เพิ่มเติมเกี่ยวกับข้อมูลของคุณ คุณจะต้องนำเข้าไปยัง PulseView

ขั้นตอนแรกคือการส่งออกไฟล์ csv ที่มีข้อมูลทั้งหมดของคุณ ในการดำเนินการดังกล่าว คุณเพียงแค่คลิกปุ่ม "ส่งออก" จากอินเทอร์เฟซบนเว็บ บันทึกไฟล์ของคุณในตำแหน่งที่รู้จักเมื่อได้รับแจ้ง

ตอนนี้เปิด PulseView ที่แถบเมนูด้านบน ให้คลิกที่ "เปิด" (ไอคอนโฟลเดอร์) แล้วเลือก "นำเข้าค่าที่คั่นด้วยเครื่องหมายจุลภาค…" เลือกไฟล์ csv ที่สร้างไว้ก่อนหน้านี้ซึ่งมีข้อมูลของคุณ

หน้าต่างเล็ก ๆ จะปรากฏขึ้น ปล่อยให้ทุกอย่างเป็นเหมือนเดิม คุณเพียงแค่ต้องแก้ไขการตั้งค่า "สุ่มตัวอย่าง" ตามปัจจัยการแบ่งนาฬิกาที่เลือกสำหรับการจับภาพ ความถี่สุ่มตัวอย่างของคุณจะเป็น "16MHz/(ปัจจัยการแบ่ง)" จากนั้นคลิกที่ "ตกลง" สัญญาณของคุณควรปรากฏบนหน้าจอ

ขั้นตอนที่ 7: การวิเคราะห์สัญญาณ PulseView

การวิเคราะห์สัญญาณ PulseView
การวิเคราะห์สัญญาณ PulseView

PulseView มีตัวถอดรหัสโปรโตคอลมากมาย หากต้องการเข้าถึง ให้คลิกที่ "เพิ่มตัวถอดรหัสโปรโตคอล" ในแถบเมนูด้านบน (เครื่องมือขวาสุด) สำหรับการทดสอบของฉัน ฉันเพิ่งส่งข้อความ UART ธรรมดาที่ 9600 บอด ดังนั้นฉันจึงค้นหา "UART"

จะเพิ่มช่องที่มีแท็กทางด้านซ้าย (เช่นเดียวกับช่องสำหรับข้อมูลของคุณ) คุณสามารถเปลี่ยนการตั้งค่าตัวถอดรหัสได้โดยคลิกที่แท็ก หลังจากเลือกข้อความที่ถูกต้องแล้ว ฉันสามารถเรียกข้อความเดียวกับข้อความที่ส่งมาจากอุปกรณ์ทดสอบของฉัน นี่แสดงว่าทั้งระบบทำงานตามที่คาดไว้

ขั้นตอนที่ 8: สรุป

บทสรุป
บทสรุป

แม้ว่าโครงการนี้จะเป็นการทดลองในตอนแรก ฉันก็พอใจกับผลลัพธ์ที่ได้รับ ฉันสามารถสุ่มตัวอย่างสัญญาณ UART ที่ความเร็วสูงสุด 115200 บอดในโหมดขอบ "ทั้งสอง" โดยไม่มีปัญหาใดๆ และฉันยังจัดการได้ถึง 230400 บอดในโหมดขอบ "ล้ม" คุณสามารถดูการตั้งค่าการทดสอบของฉันได้จากภาพด้านบน

การใช้งานของฉันมีข้อเสียหลายประการ โดยเริ่มจากความจริงที่ว่ามันสามารถจับสัญญาณได้ครั้งละหนึ่งสัญญาณเท่านั้น เนื่องจากมีเพียงพิน 8 ของ Arduino UNO เท่านั้นที่ "สามารถจับอินพุตได้" หากคุณกำลังค้นหาตัววิเคราะห์ลอจิก Arduino ที่มีช่องสัญญาณมากขึ้น ให้ไปที่ Catoblepas'

คุณไม่สามารถคาดหวังได้ว่า Arduino UNO จะสามารถจับสัญญาณที่มีความถี่สูง (MHz บางตัว) ได้ เนื่องจากมีการโอเวอร์คล็อกที่ 16MHz เท่านั้น (ถ้าใครทำ ฉันสนใจที่จะเห็นวิธีการของมัน) อย่างไรก็ตาม ฉันยังคงประทับใจกับผลลัพธ์ที่ได้จากไมโครคอนโทรลเลอร์ ATMEGA328P นี้

ฉันไม่คิดว่าฉันจะทำงานมากกับรหัส ฉันทำการทดลองและได้ผลลัพธ์ที่ต้องการ แต่ถ้าใครต้องการมีส่วนร่วม อย่าลังเลที่จะแก้ไขและแจกจ่ายรหัสของฉันทั้งหมดหรือบางส่วนอีกครั้ง

นั่นคือคำแนะนำแรกของฉันและฉันคิดว่าเป็นเวลานาน ฉันหวังว่ามันจะเป็นการอ่านที่น่าสนใจสำหรับคุณ

แจ้งให้เราทราบหากคุณพบข้อผิดพลาดหรือหากคุณมีคำถามใดๆ