Monday, October 15, 2012

Modbus Protocal

โปรโตคอล MODBUS เป็นโปรโตคอลเพื่อสื่อสารข้อมูลอินพุต/เอาต์พุตและรีจีสเตอร์ภายใน PLC ซึ่งถูกคิดค้นโดย Modicon (ปัจจุบันคือบริษัท Schneider Electric) โปรโตคอล MODBUS ได้เป็นที่ยอมรับกันอย่างกว้างขวางในการติดต่อสื่อสารที่เป็นแบบ Network Protocol อันเนื่องมาจาก MODBUS เป็นระบบเปิด, ไม่มีค่าใช้จ่าย, เชื่อมต่อและพัฒนาง่าย พร้อมทั้งยังสามารถนำโปรโตรคอลนี้ไปใช้งานในอุปกรณ์อื่นๆ เช่น Digital Power Meter, RTU (Remote Terminal Unit), Remote I/O, PLC เป็นต้น นอกจากนี้ MODBUS ยังสามารถรองรับและใช้งานร่วมกับ Application จำพวก SCADA และ HMI Software ได้อีกด้วย
โปรโตคอล MODBUS เป็นการสื่อสารข้อมูลในลักษณะ Master/Slave ซึ่งเป็นการสื่อสารจากอุปกรณ์แม่ (Master) เครื่องเดียว ส่วนใหญ่มักเป็นซอฟต์แวร์คอมพิวเตอร์หรืออุปกรณ์แสดงผล HMI ไปยังอุปกรณ์ลูก (Slave) ได้หลาย ๆ เครื่อง โดยสามารถกำหนดหมายเลขอุปกรณ์ได้สูงสุด 255 เครื่อง โดยมีลักษณะการส่งข้อมูล 2 แบบ คือ ข้อมูลแบบแอสกี (ASCII) และข้อมูลแบบเลขฐานสอง (Binary) ในโปรโตคอล MODBUS ที่สื่อสารข้อมูลแบบ ASCII จะเรียก MODBUS ASCII และโปรโตคอล MODBUS ที่สื่อสารข้อมูลแบบเลขฐานสอง จะเรียก MODBUS RTU ทำให้มีความแตกต่างในการกำหนดค่าพอร์ตสื่อสาร

การรับส่งข้อมูลด้วยโปรโตคอล MODBUS สามารถเลือกได้ 2 โหมด คือ โหมด ASCII และโหมด RTU ซึ่งทั้ง 2 โหมดนี้มีความแตกต่างกันที่การกำหนดรูปแบบของชุดข้อมูลภายในเฟรม จะเลือกโหมดใดก็ได้แต่มีเงื่อนไขว่า อุปกรณ์ทุกตัวที่ต่อร่วมกันอยู่ในบัสหรือเครือข่ายเดียวกัน จะต้องตั้งให้เลือกใช้โหมดเดียวกันทั้งหมด
การติดต่อสื่อสารแบบ Master/Slave


MODBUS RTU
เฟรมข้อมูลในโหมด RTU ประกอบด้วยข้อมูลแสดงตำแหน่งแอดเดรส 1 ไบต์, หมายเลขฟังก์ชัน 1 ไบต์, ข้อมูลที่ทำการรับส่งจำนวนมากสุดไม่เกิน 252 ไบต์ และรหัสตรวจสอบความถูกต้องของข้อมูลแบบ CRC (Cyclical Redundancy Checking) ขนาด 2 ไบต์ ค่า CRC นี้เป็นค่าที่คำนวณมาจากข้อมูลทุกไบต์ ไม่รวมบิต Start, Stop และ Parity Check โดยที่ตัว Slave ตัวที่ส่งข้อมูลออกมาจะสร้างรหัส CRC แล้วส่งตามท้ายไบต์ข้อมูลออกมา หลังจากนั้นเม่อ Master ได้รับเฟรมข้อมูลและถอดข้อมูลออกจากเฟรมแล้วจะทำการคำนวณค่า CRC ตามสูตรเดียวกับ Slave เพื่อทำการเปรียบเทียบค่า CRC ทั้ง 2 ค่าว่าตรงกันหรือไม่ หากไม่ตรงกันแสดงว่าเกิดความผิดพลาดในการรับส่งข้อมูลในโหมด RTU การรับส่งข้อมูล 1 ไบต์ ไม่ว่าจะเป็นข้อมูลส่วนใดภายในเฟรมจะต้องทำการส่งบิตข้อมูลรวม 11 บิต คือ บิตเริ่มต้น (Start) 1 บิต, บิตข้อมูล 8 บิต, บิตตรวจสอบ Parity ของข้อมูล 1 บิตและบิตหยุด 1 บิต (Stop) 1 บิต หรือหากเลือกแบบไม่มีบิต Parity ก็จะเป็นแบบ Stop แทน 2 บิต สำหรับการกำหนดให้มีบิต Parity นั้น สามารถเลือกเป็นแบบคู่ (Even Parity) หรือคี่ (Odd Parity) ก็ได้ และหากต้องการออกแบบให้สอดคล้องกับอุปกรณ์ที่มีใช้กันทั่วไปมากที่สุด ควรเลือกแบบคู่โดยที่สามารถปรับเปลี่ยนเป็นแบบคี่หรือไม่มีการตรวจสอบ Parity (No Parity) ได้ด้วย
ลักษณะเฟรมข้อมูลของ MODBUS RTU
ลักษณะข้อมูลแต่ละไบต์ของ MODBUS RTU

MODBUS ASCII
การรับส่งข้มูลในโหมด ASCII นั้นมีความแตกต่างจากโหมด RTU ตรงที่ในโหมด RTU ข้อมูลที่จะส่งขนาด 1 ไบต์ นำมารวมกับบิตประกอบต่างๆ ก็สามารถส่งออกไปได้เลย แต่สำหรับโหมด ASCII จะมองข้อมูล 1 ไบต์ นั้นออกมาเป็นตัวอักษร 2 ตัว เช่น ค่า 0x5B ซึ่งเป็นเลขฐานสิบหก ก็จะถูกมองเป็นตัวอักษร ‘5’ และตัวอักษร ‘B’ จากนั้นก็จะทำการค้นหารหัส ASCII ของตัวอักษรทั้ง 2 ตัวนั้น ซึ่งได้แก่ 0×35 สำหรับ ‘5’ และ 0×42 สำหรับ ‘B’ แล้วทำการส่งรหัส ASCII ทั้ง 2 ค่านี้ออกไป ซึ่งจะได้ผลเท่ากับการส่งค่า 0x5B ซึ่งเป็นข้อมูลขนาด 1 ไบต์ ในโหมด RTU

จะเห็นได้ว่าการส่งข้อมูลในโหมด ASCII จะต้องทำงานมากกว่าการส่งข้อมูลในโหมด RTU ซึ่งทำให้อัตราเร็วในการสื่อสารมีค่าต่ำกว่า สาเหตุที่เป็นแบบนี้ก็เพราะว่า โหมด ASCII ได้ถูกออกแบบมาสำหรับอุปกรณ์ที่ไม่มีความสามารถในการกำหนดช่วงระยะห่างของ เวลาในการส่งเฟรมข้อมูล อย่างเช่นในโหมด RTU ที่อุปกรณ์สามารถกำหนดได้ว่าจะส่งเฟรมข้อมูลแต่ละเฟรมออกมาด้วยเวลาห่างกัน เท่าใด และอุปกรณ์ที่รอรับข้อมูลก็ต้องสามารถตรวจจับและแยกแยะได้ว่าเฟรมข้อมูลแต่ ละเฟรมที่รับเข้ามานั้นมีระยะเวลาห่างกันภายในช่วงเวลาที่กำหนดหรือไม่ เพื่อทำให้สามารถตรวจสอบหาจุดเริ่มต้นและจุดสิ้นสุดของเฟรมข้อมูลแต่ละเฟรม ได้ แต่ในความเป็นจริงยังมีอุปกรณ์อีกหลายชนิดที่ไม่มีความสามารถพิเศษนี้ จึงต้องใช้วิธีอื่นที่จะช่วยให้สามารถรับรู้จุดเริ่มต้นและจุดสิ้นสุดของ เฟรมข้อมูลได้ นั่นได้แก่โหมด ASCII ซึ่งในโหมดนี้จะเริ่มต้นเฟรมข้อมูลด้วยการส่งรหัส ASCII ที่กำหนดให้หมายถึงจุดเริ่มต้น คือ 0x3A ซึ่งตรงกับตัวอักษร ‘:’ ตามด้วยแอดเดรสของ Slave, หมายเลขฟังก์ชัน, ข้อมูล, รหัสตรวจสอง RLC และรหัส ASCII 2 ตัว ที่กำหนดให้หมายถึงจุดสิ้นสุด คือ รหัส 0x0D และ 0x0A คือรหัส CR (Carriage Return) และ LF (Line Feed) ตามลำดับ โดยในขณะที่บัสข้อมูลว่างจากการรับส่งข้อมูล อุปกรณ์ทุกตัวจะคอยตรวจสอบข้อมูลในบัสว่ามีการส่งรหัส ASCII ของ ‘:’ ออกมาหรือไม่ ถ้ามีก็จะรับรู้ว่าขณะนี้ได้มีการเริ่มต้นส่งเฟรมข้อมูลออกมาแล้ว ก็จะเข้ากระบวนการรับข้อมูลต่อไป
ลักษณะเฟรมข้อมูลของ MODBUS ASCII
ลักษณะข้อมูลแต่ละไบต์ของ MODBUS ASCII

MODBUS จะบริการให้อุปกรณ์ติดต่อสื่อสารกันผ่าน Serial Port (RS-232/422/485) แต่ในปัจจุบันได้มีการพัฒนาให้อุปกรณ์สามารถติดต่อสื่อสารกับอุปกรณ์ที่อยู่ บนเครือข่าย Ethernet ซึ่งอุปกรณ์ที่ใช้การสื่อสารแบบ MODBUS Protocol ส่วนใหญ่จะเป็น PLCs, DCSs, HMIs, Instruments อย่างไรก็ตาม MODBUS จำเป็นต้องมีอุปกรณ์จำพวก Gateway และ Bridge ในการติดต่อสื่อสารระหว่าง Serial Line กับ Ethernet


MODBUS TCP/IP
MODBUS TCP/IP ถูกพัฒนาขึ้นโดยมีวัตถุประสงค์เพื่อจะนำการสื่อสารแบบ Internet มาใช้กับอุปกรณ์จำพวก Ethernet Device ระยะในการใช้งานสำหรับการเดินสาย (สาย LAN) คือ 100 เมตร โดยสามารถขยายระยะในการสื่อสารได้โดยการใช้อุปกรณ์ Repeater หรือในระบบ LAN จะเรียกอุปกรณ์นี้ว่า Hub หรือ Switch ก็จะสามารถลากสายได้อีก 100 เมตร และยังสามารถต่อ Repeater ขยายระยะทางได้โดยไม่จำกัด ในการสื่อสารโดยทั่วไปมีความเร็ว 100,000,000 บิตต่อวินาที (100 Mbps) และเชื่อมต่ออุปกรณ์ได้ไม่จำกัดจำนวน




MODBUS TCP/IP
MODBUS ASCII/RTU ที่จะติดต่อสื่อสารกับ MODBUS TCP เพื่อให้ใช้งานในเครือข่าย Ethernet จะใช้ Gateway ติดต่อและแปลงรูปแบบการสื่อสารข้อมูล โดยการสื่อสารของ MODBUS RTU/ASCII จะเป็นการสื่อสารผ่านทาง RS-232/422/485 นั้นจะถูก Gateway แปลงให้เป็น MODBUS TCP เพื่อใช้ในการติดต่อสื่อสารในเครือข่าย Ethernet ต่อไป



การแปลง MODBUS Serial เป็น MODBUS Ethernet
credits : http://riverplusblog.com/2011/08/18/plc-protocol-การสื่อสารแบบ-modbus-protocol/

Friday, September 7, 2012

AVR Input Capture ( ICP ) เอาไปใช้นับ RPM/Frequency

ตอนที่ 1
บทนำ
เนื่องจากเห็นมีคำถามเรื่่องนี้มาหลายหน และคิดว่า ความรู้ด้านนี้จะมีประโยชน์และสามารถเอาไปใช้ได้ในงานหลายๆแบบ ก็เลย

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

มันเป็นวิธีการในการจับเวลาของ  interrupt นั่นก็คือเมื่อมีเหตุการณ์ที่เกิดขึ้นจากภายนอกตัว Microcontriller ( mcu)  ดังนั้น หากเราจับเวลา ว่า เหตุการแรกเกิดขึ้นเมื่อใดแล้ว  และจับเวลาของเหตุการณ์ที่เกิดขึ้นต่อมา เอาเวลามาหักลบกัน ก็สามารถบอกว่า เหตุการณ์ ที่เกิดขึ้นห่างกันเท่าไร

จากข้อมูลที่ได้ เราสามารถเอาไปใช้ได้หลายๆอย่าง เช่นเอาไปคำนวณ ความถี่ ( frequency ), duty cycle, และอื่นๆ อีกเช่น RPM ของใบจักร หรือกังหัน เป็นต้น

Credit
ผู้เขียนได้รับทราบถึง การใช้ Input Capture ในการวัดรอบของใบพัด จากการชี้แนะของท่าน Firmware.c ในหลายๆกระทู้ที่เกี่ยวกับการวัดในเรื่องแบบนี้ใน electoday.com ทำให้ต้องไปหาอ่าน ศึกษาและทดสอบ จนเข้าใจและใช้มันได้ ต้องขอขอบคุณมาในที่นี้ด้วย

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

เริ่มจาก ใน AVR ในที่นี้จะใช้ AVR atmega168 เป็นหลัก ซึ่งก็จะใช้กับ atmega 48/88 หรือ 328 ก็ได้ เพราะใช้ register ใน microcontroller ตัวเดียวกัน ( atmega8 นั้น ตัว register อาจจะต่างไปเพียงเล็กน้อย)

ทฤษฎี
( ข้อมูลทั้งหมด เอามาจาก datasheet เรื่องการใช้ ICP นี้ จะอยู่ในหัวข้อของ Timer/counter1 (TCNT1) บทที่ 15 ในบทย่อย 15.6 )

หากดูตามชื่อของมัน ว่า Input Capture interrupt ก็คือ การจับเหตุการณ์ จากภายนอก ( input) หากเราไปดูที่ chip ของ atmega48/88/168/328 จะเห็นว่า pin ที่ว่า นี้คือ PB0 ดังนั้น เราก็ต้องเอาสายจากโลกภายนอก ที่เราต้องการจะคอยจ้องจับเวลาที่มันเกิดเหตุการณ์ เข้า ตรง pin นี้

ก่อนอื่นต้องเข้าใจก่อนว่า การทำงานนี้ อาศัย Timer/counter1 มันจะเริ่มทำงานนับเวลาของมันไปเรื่อย และจะเอาค่านับนี้ไปเก็บใน register ในที่นี้ คือ TCNT1 ซึ่งเราสามารถจะกำหนดค่าเริ่มต้น ให้มันได้ เช่น ให้เริ่มจาก 0 เป็นต้น เนื่องจาก TCNT1 นี้ มันมีขนาด 16 บิท ดังนั้น ค่าที่มันสามารถนับได้สูงสุดก็คือ 65535 นี่คือ นาฬิกาของเรา และเมื่อมันนับจนล้น คือเกิน 65535 มันก็จะกลับไปเริ่มที่ 0 ใหม่
Fig1


รายละเอียดของมันมีอยู่ว่า เมื่อเราติดตั้งต่างๆ ( register setup) เสร้จแล้ว เมื่อมี interrupt เกิดขึ้น มันจะ เอาค่าใน TCNT1 นี้ไปเก็บใน Input Capture register ( ICR1 ) ดังนั้น หากเราจับเวลาได้จากสองช่วงเวลา เช่น จับได้ว่า ใบจักร หมุนไปจากจุดแรก ( ซี่งจะใช้ IR sensor หรือ จะใช้ Hall Sensor เป็นตัวจับ ) แล้ววนกลับมาหาจุดเดิมอีก เราก็สามารถคำนวนหา frequency ( Hz)  หรือ RPM หรือ เวลา ต่อ หนึ่งรอบหมุน

REGISTERS ต่างๆที่ต้องใช้
ทั้งหมดที่จะต้องใช้นั้น ส่วนใหญ่จะอยู่ในกลุ่มของ Timer/Counter1 คือ

Timer/Counter1 Control Register A (TCCR1A)
Fig2


จากในรูป จะเห็นว่า เราจะไม่ใช้ bit 2 - 6
สำหรับ WGM11-WGM10 (bit 0-1) นั้น เป็นตัวที่จะบอกว่า เราจะให้ timer ของเราวิ่งใน mode ใหน โดยจะใช้ร่วมกับ         WGM12 & WGM13 ซึ่งไปอยู่ใน TCCR1B

Fig 3 mode


จาก ตาราง mode ที่เห็น เราจะใช้ normal mode กล่าวคือ ให้ counter มันวิ่งจากค่าล่างสุด (0) ไปจนถึงค่าสูงสุด
ดังนั้น ค่า WGM13 ถึง WGM10 จะเป็น 0 หมด
TCCR1A จะกำหนดให้เป็น 0

Timer/Counter1 Control Register B (TCCR1B)
Fig 4


ในรูปนี้ แสดงถึง register ที่เราต้องติดตั้งค่าเพื่อใช้ใน ICP มากหน่อย
จะเห็นว่า bit 3-4 นั้นมันไปโยงกับ WGM13-12 ตามที่ว่ามาข้างบน ซึ่งจะถูกกำหนดให้เป็น 0
ส่วน bit 0-1-2 นั้นคือ CS ซึ่งเป็นตัวบอกว่า เราต้องการจะชลอ Counter ซึ่งปกติจะวิ่งเท่ากับ sysetm clock ( prescale) ในกรณีของเรา จะชลอโดยให้ หารด้วย 256 ค่าที่ต้องใช้ ก็คือ 100 ดังนั้น ความเร็วของ clock ทีใช้ใน ICP นี้ก็จะออกมา ( เราจะใช้ Arduino ฉนั้น system clock = 16 MHz )
16000000 / 256 == 62500 หรือ จะกล่าวอีกอย่างก็คือ ใน 1 sec นั้น นาฬิกา( counter ที่เราเลือกใช้) จะเดินในความเร็ว 62500 clock cycles
นี่คือค่าของช่วงเวลาที่เราจะเอามาใช้คำนวน จากที่เหตุการณ์เกิดขึ้นจากการใช้ clock speed ที่เราทอนลงมา ตัวอย่าง ว่า เราวัดช่วงเวลา ระหว่างสองเหตุการณ์ เช่น หนึ่งรอบหมุนของใบพัด ได้ 316 clock cycle  เราก็สามารถหาเวลา ว่า เท่าไร โดย
316 clock tick * 1000 millisec / 62500 = 5.056 millisecond หรือ
จะทอนกลับเป็น frequency = 1000/5.056 ~ 197 Hz
หากจะคิดเป็น RPM ก็จะได้ว่า 197 * 60 == 11867( เกือบจะเป็น 12K)  RPM เป็นต้น

bit ที่เราต้องใช้อีก ก็คือ bit 5 หรือ ICES1 เป็นการเลื่อก EDGE SELECT
หากเรา set มัน ก็เป็นการบอกว่า จะให้  ICP1 interrupt เกิดขึ้นแบบ ให้กระตุ้นตอนขาขึ้น rising หรือ
จะให้กระตุ้น ตอนขาลง ( falling )โดย clear bit นี้แสีย

ค่าของ bit นี้  ในกรณีตัวอย่างของเราจะใช้ ขาขึ้น ดังนั้น เราต้อง set ให้เป็น 1
TCCR1B |=  _BV( ICES1 // rising edge
TCCR1B |=  _BV(CS12)  // prescale ด้วย 256

Timer/Counter1 Interrupt Mask Register ( TIMSK1)
Fig5


register ตัวนี้ก็ตรงไปตรงมากล่าวคือ เราต้อง enable bit ที่ 5 ซึ่งเป็นการบ่งว่า เราต้องการใช้ Input Capture

Interrupt bit ที่เหลือ ไม่ต้องไปยุ่งเลย
TIMSK1 |= _BV(ICIE1) // enable ICP interrupt

Timer/Counter1 ( TCNT1) เป็นตัวคอยนับเวลา คือเป็น counter
Fig6


นี่คือ register ที่คอยเป็นตัวนับค่า counter หรือ นาฬิกาของเรา มันจะจัดการ update ค่าของมันเอง
หากดูตาม datasheet ตัวนี้ มันเป็น 16 bit register (ดูความหมายของ 16 bit register ข้างบน) มันจะเป็นตัวคอยนับเวลาให้เรา

ICR1 เป็นตัวที่เมื่อ มี interrupt เกิดขึ้น ค่าใน TCNT1 จะถูกเอามาเก็บใน ICR1 register เราก็ต้องมีหน้าที่คอยเก็บค่าในนี้ มาใช้ ในการคำนวน
Fig7


Register ตัวสุดท้ายที่หลายๆ interrupt ต้องใช้ก็คือ Global Register ซึ่งในที่นี้ก็คือ Interrupt flag

ของ SREG register เราสามารถ enable โดย ใช้ macro ที่ให้มากับ ave คือ sei()

สรุป code ที่เราต้อง setup
  1. TCCR1A = 0;
  2. TCCR1B |=  _BV( ICES1) ;  // rising edge
  3. TCCR1B |=  _BV(CS12)  ;  // prescale ด้วย 256
  4. TIMSK1 |= _BV(ICIE1)  ;  // enable ICP interrupt
  5. sei();  // enable global interrupt
คัดลอกไปที่คลิปบอร์ด
แค่นี้ เราก็สามารถจะ ทำงานแบบ Input Capture Interrupt ได้แล้ว
credit : http://www.electoday.com/bbs/viewthread.php?tid=14149