Wednesday, September 26, 2012
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
คัดลอกไปที่คลิปบอร์ด
แค่นี้ เราก็สามารถจะ ทำงานแบบ Input Capture Interrupt ได้แล้ว
credit : http://www.electoday.com/bbs/viewthread.php?tid=14149
บทนำ
เนื่องจากเห็นมีคำถามเรื่่องนี้มาหลายหน และคิดว่า ความรู้ด้านนี้จะมีประโยชน์และสามารถเอาไปใช้ได้ในงานหลายๆแบบ ก็เลย
จะมาเล่าให้ฟังในรูปแบบของงานทดลอง กอร์ปกับอีกประการ ผู้เขียนได้มีพรรคพวกในชมรม ที่ต้องการวัดความเร็วรอบของเครื่องในรถยนต์เขา ได้มาขอให้ผู้เขียนช่วยเขียน 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
- TCCR1A = 0;
- TCCR1B |= _BV( ICES1) ; // rising edge
- TCCR1B |= _BV(CS12) ; // prescale ด้วย 256
- TIMSK1 |= _BV(ICIE1) ; // enable ICP interrupt
- sei(); // enable global interrupt
credit : http://www.electoday.com/bbs/viewthread.php?tid=14149
Labels:
C/C++,
coding,
development,
Embedded,
Hardware,
IT,
programming,
software
Subscribe to:
Posts (Atom)