ตามที่คนอื่น ๆ ได้กล่าวไว้คุณควรพิจารณาตัวกรอง IIR (การตอบสนองต่อแรงกระตุ้นแบบไม่สิ้นสุด) มากกว่าตัวกรอง FIR (การตอบสนองต่อแรงกระตุ้นแบบ จำกัด แน่นอน) ที่คุณกำลังใช้อยู่ในขณะนี้ มีมากไปกว่านั้น แต่เมื่อใช้ตัวกรอง FIR แรกจะใช้งานอย่างชัดเจนและตัวกรอง IIR ที่มีสมการ
ตัวกรอง IIR ที่ฉันใช้เป็นจำนวนมากในไมโครคอนโทรลเลอร์เป็นตัวกรองสัญญาณความถี่ต่ำแบบเสาเดียว นี่คือดิจิตอลเทียบเท่าของตัวกรองสัญญาณอนาล็อก RC แบบง่าย สำหรับแอปพลิเคชันส่วนใหญ่สิ่งเหล่านี้จะมีลักษณะที่ดีกว่าตัวกรองกล่องที่คุณใช้ การใช้ตัวกรองกล่องส่วนใหญ่ที่ฉันพบเป็นผลมาจากคนที่ไม่ใส่ใจในชั้นเรียนการประมวลผลสัญญาณดิจิตอลไม่ได้เป็นผลมาจากความต้องการลักษณะเฉพาะของพวกเขา หากคุณต้องการลดทอนความถี่สูงที่คุณรู้ว่าเป็นเสียงตัวกรองสัญญาณความถี่ต่ำแบบเสาเดียวจะดีกว่า วิธีที่ดีที่สุดในการใช้หนึ่งดิจิตอลในไมโครคอนโทรลเลอร์มักจะ:
FILT <- FILT + FF (ใหม่ - ตัวกรอง)
FILT เป็นชิ้นส่วนของสถานะถาวร นี่เป็นตัวแปรถาวรเพียงตัวเดียวที่คุณต้องใช้ในการคำนวณตัวกรองนี้ ใหม่คือค่าใหม่ที่ตัวกรองจะถูกอัปเดตด้วยการวนซ้ำนี้ FF คือส่วนกรองซึ่งปรับ "ความหนัก" ของตัวกรอง ดูอัลกอริธึมนี้และดูว่าสำหรับ FF = 0 ตัวกรองนั้นหนักมากเนื่องจากเอาต์พุตไม่เคยเปลี่ยนแปลง สำหรับ FF = 1 จะไม่มีตัวกรองเลยจริงๆเพราะเอาต์พุตจะตามหลังอินพุต ค่าที่มีประโยชน์อยู่ในระหว่าง สำหรับระบบเล็ก ๆ คุณเลือก FF เป็น 1/2 Nดังนั้นการคูณด้วย FF สามารถทำได้โดยการเลื่อนขวาโดย N bits ตัวอย่างเช่น FF อาจเป็น 1/16 และคูณด้วย FF ดังนั้นการเปลี่ยนที่ถูกต้องคือ 4 บิต มิฉะนั้นตัวกรองนี้ต้องการเพียงหนึ่งการลบและการเพิ่มเพียงอย่างเดียวถึงแม้ว่าโดยทั่วไปแล้วตัวเลขจะต้องกว้างกว่าค่าอินพุต (เพิ่มเติมเกี่ยวกับความแม่นยำเชิงตัวเลขในส่วนที่แยกต่างหากด้านล่าง)
ฉันมักจะอ่าน A / D เร็วกว่าที่จำเป็นและใช้ตัวกรองสองตัวเหล่านี้เรียงซ้อนกัน นี่คือเทียบเท่าดิจิตอลของตัวกรอง RC สองตัวในชุดและลดทอน 12 dB / อ็อกเทฟเหนือความถี่การหมุน อย่างไรก็ตามสำหรับการอ่าน A / D มักจะมีความเกี่ยวข้องมากกว่าในการดูตัวกรองในโดเมนเวลาโดยพิจารณาจากการตอบสนองขั้นตอน สิ่งนี้จะบอกคุณว่าระบบของคุณจะเห็นการเปลี่ยนแปลงรวดเร็วเพียงใดเมื่อสิ่งที่คุณกำลังวัดการเปลี่ยนแปลง
เพื่ออำนวยความสะดวกในการออกแบบตัวกรองเหล่านี้ (ซึ่งหมายถึงการเลือก FF และตัดสินใจว่าจะกรองจำนวนเท่าใด) ฉันจึงใช้โปรแกรม FILTBITS คุณระบุจำนวนของ shift shift สำหรับ FF แต่ละตัวในชุดกรองแบบเรียงซ้อนและคำนวณการตอบสนองขั้นตอนและค่าอื่น ๆ ที่จริงฉันมักจะเรียกใช้สิ่งนี้ผ่านสคริปต์ wrapper ของฉัน PLOTFILT สิ่งนี้จะทำงานกับ FILTBITS ซึ่งสร้างไฟล์ CSV จากนั้นแปลงไฟล์ CSV ตัวอย่างเช่นนี่คือผลลัพธ์ของ "PLOTFILT 4 4":
พารามิเตอร์ทั้งสองไปยัง PLOTFILT หมายความว่าจะมีตัวกรองสองตัวเรียงตามประเภทที่อธิบายไว้ข้างต้น ค่าของ 4 ระบุจำนวนของ shift shift เพื่อรับรู้การคูณด้วย FF ดังนั้นค่า FF ทั้งสองจึงเป็น 1/16 ในกรณีนี้
ร่องรอยสีแดงคือการตอบสนองขั้นตอนของหน่วยและเป็นสิ่งสำคัญที่ต้องดู ตัวอย่างเช่นสิ่งนี้บอกคุณว่าถ้าอินพุตเปลี่ยนแปลงทันทีผลลัพธ์ของตัวกรองที่รวมกันจะชำระให้ 90% ของค่าใหม่ในการวนซ้ำ 60 ครั้ง หากคุณสนใจเวลานั่งที่ 95% คุณต้องรอการคำนวณซ้ำ 73 ครั้งและสำหรับการตั้งเวลา 50% เพียง 26 รอบเท่านั้น
การติดตามสีเขียวแสดงผลลัพธ์จากแอมพลิจูดแบบเข็มเดียว นี่จะช่วยให้คุณมีความคิดในการลดเสียงรบกวนแบบสุ่ม ดูเหมือนว่าไม่มีตัวอย่างเดียวจะทำให้เกิดการเปลี่ยนแปลงมากกว่า 2.5% ในเอาต์พุต
ร่องรอยสีน้ำเงินคือการให้ความรู้สึกส่วนตัวของสิ่งที่ตัวกรองนี้ทำกับเสียงสีขาว นี่ไม่ใช่การทดสอบที่เข้มงวดเนื่องจากไม่มีการรับประกันว่าเนื้อหาใดของตัวเลขสุ่มที่เลือกว่าเป็นสัญญาณเสียงสีขาวสำหรับการทำงานของ PLOTFILT มันเป็นเพียงการให้ความรู้สึกคร่าวๆว่ามันจะถูกบีบอัดและเรียบเนียนแค่ไหน
PLOTFILT อาจ FILTBITS และจำนวนมากของสิ่งที่มีประโยชน์อื่น ๆ โดยเฉพาะอย่างยิ่งสำหรับการพัฒนาเฟิร์มแว PIC ที่มีอยู่ในการเปิดตัวซอฟต์แวร์เครื่องมือพัฒนา PIC ที่ของฉันหน้าดาวน์โหลดซอฟแวร์
เพิ่มเกี่ยวกับความแม่นยำเชิงตัวเลข
ฉันเห็นจากความคิดเห็นและตอนนี้คำตอบใหม่ที่มีความสนใจในการหารือจำนวนบิตที่จำเป็นในการใช้ตัวกรองนี้ โปรดทราบว่าการคูณด้วย FF จะสร้างบิตใหม่Log 2 (FF) ใต้จุดไบนารี่ ในระบบขนาดเล็ก FF มักถูกเลือกให้เป็น 1/2 Nเพื่อให้การคูณนี้เกิดขึ้นจริงโดยการเลื่อน N บิตที่ถูกต้อง
FILT จึงมักจะเป็นจำนวนเต็มจุดคงที่ โปรดทราบว่านี่จะไม่เปลี่ยนคณิตศาสตร์ใด ๆ จากมุมมองของโปรเซสเซอร์ ตัวอย่างเช่นหากคุณกำลังกรองการอ่าน A / D 10 บิตและ N = 4 (FF = 1/16) ดังนั้นคุณต้องมีเศษส่วน 4 บิตใต้การอ่าน A / D จำนวนเต็ม 10 บิต หนึ่งโปรเซสเซอร์ส่วนใหญ่คุณจะทำการดำเนินการจำนวนเต็ม 16 บิตเนื่องจากการอ่าน A / D 10 บิต ในกรณีนี้คุณยังคงสามารถทำจำนวนเต็ม 16 บิต แต่เริ่มต้นด้วยการอ่าน A / D ที่เลื่อนไปทางซ้าย 4 บิต โปรเซสเซอร์ไม่ทราบความแตกต่างและไม่จำเป็นต้อง การดำเนินการทางคณิตศาสตร์กับจำนวนเต็ม 16 บิตทั้งที่ทำงานไม่ว่าคุณจะคิดว่ามันเป็น 12.4 จุดคงที่หรือเป็นจำนวนเต็ม 16 บิตจริง (16.0 จุดคงที่)
โดยทั่วไปคุณต้องเพิ่ม N บิตแต่ละขั้วกรองหากคุณไม่ต้องการเพิ่มเสียงรบกวนเนื่องจากการแสดงตัวเลข ในตัวอย่างข้างต้นตัวกรองตัวที่สองของทั้งสองจะต้องมี 10 + 4 + 4 = 18 บิตเพื่อไม่ให้ข้อมูลสูญหาย ในทางปฏิบัติกับเครื่อง 8 บิตซึ่งหมายความว่าคุณจะใช้ค่า 24 บิต ในทางเทคนิคเพียงขั้วที่สองของทั้งสองจะต้องการค่าที่กว้างขึ้น แต่สำหรับความเรียบง่ายของเฟิร์มแวร์ฉันมักจะใช้การแทนแบบเดียวกันและด้วยเหตุนี้จึงใช้รหัสเดียวกันสำหรับทุกขั้วของตัวกรอง
โดยปกติฉันจะเขียนรูทีนย่อยหรือมาโครเพื่อดำเนินการหนึ่งขั้วกรองจากนั้นนำไปใช้กับแต่ละขั้ว รูทีนย่อยหรือมาโครขึ้นอยู่กับว่าวงจรหรือหน่วยความจำของโปรแกรมมีความสำคัญมากกว่าในโครงการนั้นหรือไม่ ฉันใช้สถานะรอยขีดข่วนบางส่วนเพื่อส่งผ่าน NEW ไปยังรูทีนย่อย / มาโครซึ่งอัพเดต FILT แต่ยังโหลดว่าอยู่ในสถานะรอยขีดข่วนเดียวกัน NEW อยู่ระบบนี้ทำให้ง่ายต่อการใช้หลายขั้วตั้งแต่การปรับปรุง FILT ของหนึ่งขั้วคือ ใหม่ของถัดไป เมื่อรูทีนย่อยมีประโยชน์ที่จะมีจุดพอยน์เตอร์ไปที่ FILT ซึ่งเป็นสิ่งที่อัปเดตเป็นหลัง FILT เมื่อทางออก ด้วยวิธีนี้รูทีนย่อยจะทำงานโดยอัตโนมัติในตัวกรองที่ต่อเนื่องกันในหน่วยความจำหากเรียกหลายครั้ง ด้วยแมโครคุณไม่จำเป็นต้องมีตัวชี้เนื่องจากคุณผ่านที่อยู่เพื่อดำเนินการกับการวนซ้ำแต่ละครั้ง
ตัวอย่างรหัส
นี่คือตัวอย่างของมาโครตามที่อธิบายไว้ข้างต้นสำหรับ PIC 18:
////////////////////////////////////////////////// //////////////////////////////
//
// ตัวกรองมาโคร
//
// อัปเดตหนึ่งขั้วกรองด้วยค่าใหม่ใน NEWVAL NEWVAL ได้รับการอัปเดตเป็น
// มีค่าตัวกรองใหม่
//
// FILT เป็นชื่อของตัวแปรสถานะตัวกรอง จะถือว่าเป็น 24 บิต
// กว้างและในธนาคารท้องถิ่น
//
// สูตรการอัพเดตตัวกรองคือ:
//
// FILT <- FILT + FF (NEWVAL - FILT)
//
// การคูณด้วย FF สามารถทำได้โดยการเปลี่ยนบิตของฟิลท์บิตต์
//
/ ตัวกรองมาโคร
/เขียน
dbankif lbankadr
movf [arg 1] +0, w; NEWVAL <- NEWVAL - FILT
subwf newval + 0
movf [arg 1] +1, w
subwfb newval + 1
movf [arg 1] +2, w
subwfb newval + 2
/เขียน
/ loop n filtbits หนึ่งครั้งต่อการเลื่อน NEWVAL ไปทางขวาหนึ่งครั้ง
rlcf newval + 2, w; เลื่อน NEWVAL ไปทางขวาหนึ่งบิต
rrcf newval + 2
rrcf newval + 1
rrcf newval + 0
/ endloop
/เขียน
movf newval + 0, w; เพิ่มค่า shifted ลงในตัวกรองและบันทึกใน NEWVAL
addwf [arg 1] +0, w
movwf [arg 1] +0
movwf newval + 0
movf newval + 1, w
addwfc [arg 1] +1, w
movwf [arg 1] +1
movwf newval + 1
movf newval + 2, w
addwfc [arg 1] +2, w
movwf [arg 1] +2
movwf newval + 2
/ endmac
และนี่คือมาโครที่คล้ายกันสำหรับ PIC 24 หรือ dsPIC 30 หรือ 33:
////////////////////////////////////////////////// //////////////////////////////
//
// ตัวกรองมาโคร ffbits
//
// อัปเดตสถานะของตัวกรองความถี่ต่ำหนึ่งตัว ค่าอินพุตใหม่อยู่ใน W1: W0
// และสถานะตัวกรองที่จะอัปเดตนั้นชี้ไปที่ W2
//
// ค่าตัวกรองที่อัปเดตจะถูกส่งคืนใน W1: W0 และ W2 ด้วย
// ไปยังหน่วยความจำแรกที่ผ่านสถานะตัวกรอง มาโครนี้จึงสามารถ
// เรียกใช้อย่างต่อเนื่องเพื่ออัปเดตชุดตัวกรอง low pass แบบต่อเนื่อง
//
// สูตรตัวกรองคือ:
//
// FILT <- FILT + FF (ใหม่ - ตัวกรอง)
//
// โดยที่การคูณด้วย FF จะถูกดำเนินการโดยการคำนวณทางขวาของ
// FFBITS
//
// คำเตือน: W3 ถูกทิ้งในถังขยะ
//
/ ตัวกรองมาโคร
/ var ใหม่ ffbits จำนวนเต็ม = [หา 1]; รับจำนวนบิตที่จะเปลี่ยน
/เขียน
/ write "; ดำเนินการกรองผ่านความถี่ต่ำหนึ่งขั้ว, shift bits =" ffbits
/ เขียน ";"
ย่อย w0, [w2 ++], w0; ใหม่ - ตัวกรอง -> W1: W0
subb w1, [w2--], w1
lsr w0, # [v ffbits], w0; เลื่อนผลลัพธ์เป็น W1: W0 ขวา
sl w1, # [- 16 ffbits], w3
หรือ w0, w3, w0
เช่น w1, # [v ffbits], w1
เพิ่ม w0, [w2 ++], w0; เพิ่ม FILT เพื่อสร้างผลลัพธ์สุดท้ายใน W1: W0
addc w1, [w2--], w1
mov w0, [w2 ++]; เขียนผลลัพธ์ไปยังสถานะตัวกรอง, ตัวชี้ล่วงหน้า
mov w1, [w2 ++]
/เขียน
/ endmac
ตัวอย่างทั้งสองนี้มีการใช้งานเป็นมาโครโดยใช้ตัวประมวลผลล่วงหน้าแอสเซมเบลอร์ PICของฉันซึ่งมีความสามารถมากกว่าสิ่งอำนวยความสะดวกในตัวแมโครอย่างใดอย่างหนึ่ง