หลีกเลี่ยงตัวแปรโกลบอลเมื่อใช้อินเตอร์รัปต์ในระบบฝังตัว


13

มีวิธีที่ดีในการใช้การสื่อสารระหว่าง ISR และส่วนที่เหลือของโปรแกรมสำหรับระบบฝังตัวที่หลีกเลี่ยงตัวแปรทั่วโลกหรือไม่?

ดูเหมือนว่ารูปแบบทั่วไปคือการมีตัวแปรทั่วโลกซึ่งใช้ร่วมกันระหว่าง ISR และส่วนที่เหลือของโปรแกรมและใช้เป็นธง แต่การใช้ตัวแปรทั่วโลกนี้ขัดแย้งกับธัญพืชให้ฉัน ฉันได้รวมตัวอย่างง่ายๆโดยใช้ ISR สไตล์ avr-libc:

volatile uint8_t flag;

int main() {
    ...

    if (flag == 1) {
        ...
    }
    ...
}

ISR(...) {
    ...
    flag = 1;
    ...
}

ฉันไม่สามารถเห็นสิ่งที่เป็นปัญหาในการกำหนดขอบเขต ตัวแปรใดที่สามารถเข้าถึงได้โดยทั้ง ISR และส่วนที่เหลือของโปรแกรมจะต้องเป็นสากลโดยแท้จริง? แม้จะมีสิ่งนี้ฉันก็มักจะเห็นคนพูดถึงสิ่งต่าง ๆ ตามแนว "ตัวแปรทั่วโลกเป็นวิธีหนึ่งในการนำการสื่อสารระหว่าง ISRs และส่วนที่เหลือของโปรแกรม" (เน้นที่เหมือง) ซึ่งดูเหมือนจะบ่งบอกว่ามีวิธีอื่น ๆ ถ้ามีวิธีอื่นพวกมันคืออะไร?



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

1
ข้างธงจะต้องประกาศความผันผวนเพราะคุณกำลังใช้ / เปลี่ยนมันนอกกระแสโปรแกรมปกติ สิ่งนี้บังคับให้คอมไพเลอร์ไม่ปรับการอ่าน / เขียนไปยังแฟล็กและปรับการดำเนินการอ่าน / เขียนตามความเป็นจริง
next-hack

@ next-hack ใช่ถูกต้องจริง ๆ ขอโทษด้วยฉันแค่พยายามหาตัวอย่างเร็ว ๆ

คำตอบ:


18

มีวิธีมาตรฐานอย่างแท้จริงในการทำเช่นนี้ (สมมติว่าการเขียนโปรแกรม C):

  • อินเตอร์รัปต์ / ISR อยู่ในระดับต่ำและควรนำมาใช้ภายในไดร์เวอร์ที่เกี่ยวข้องกับฮาร์ดแวร์ที่สร้างอินเทอร์รัปต์ พวกเขาไม่ควรอยู่ที่อื่น แต่อยู่ในไดรเวอร์นั้น
  • การสื่อสารกับ ISR ทั้งหมดกระทำโดยไดรเวอร์และไดรเวอร์เท่านั้น หากส่วนอื่น ๆ ของโปรแกรมต้องการเข้าถึงข้อมูลดังกล่าวก็ต้องขอจากไดรเวอร์ผ่านฟังก์ชั่น setter / getter หรือสิ่งที่คล้ายกัน
  • คุณไม่ควรประกาศตัวแปร "global" ตัวแปรขอบเขตไฟล์หมายถึงทั่วโลกที่มีการเชื่อมโยงภายนอก นั่นคือ: ตัวแปรที่สามารถเรียกใช้ได้ด้วยexternคำหลักหรือโดยไม่ได้ตั้งใจ
  • แต่จะบังคับให้ encapsulation ส่วนตัวภายในไดรเวอร์ตัวแปรดังกล่าวทั้งหมดร่วมกันระหว่างผู้ขับขี่และ ISR staticจะได้รับการประกาศ ตัวแปรดังกล่าวไม่ใช่แบบโกลบอล แต่ จำกัด เฉพาะไฟล์ที่มีการประกาศ
  • เพื่อป้องกันปัญหาการเพิ่มประสิทธิภาพของคอมไพเลอร์ variabels volatileดังกล่าวควรได้รับการประกาศให้เป็น หมายเหตุ: นี่ไม่ได้ให้สิทธิ์การเข้าถึงแบบปรมาณูหรือแก้ปัญหาเรื่องการเข้าใหม่
  • กลไกการเข้าสู่ระบบซ้ำบางครั้งเป็นสิ่งจำเป็นในไดร์เวอร์ในกรณีที่ ISR เขียนไปยังตัวแปร ตัวอย่าง: การปิดใช้งานการขัดจังหวะ, การขัดจังหวะโดยรวมทั่วโลก, สัญญาณ / mutex หรือการอ่านปรมาณูที่รับประกัน

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

สิ่งที่คุณจะพูดว่าถ้าโต้แย้งเป็นค่าใช้จ่ายที่เพิ่มขึ้น (และรหัสพิเศษ) ของการใช้ฟังก์ชั่น setter / รับ? ฉันไปที่ตัวเองคิดเกี่ยวกับมาตรฐานรหัสสำหรับอุปกรณ์ฝังตัว 8 บิตของเรา
Leroy105

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

2
ที่ถูกกล่าวว่าในกรณีของการเขียนไดรเวอร์ ISR โปรแกรมเมอร์ประมาณ 80-90% ของโปรแกรมเมอร์ทั้งหมด (ไม่ได้พูดเกินจริงที่นี่) มักจะมีบางอย่างผิดปกติ ผลที่ได้คือข้อผิดพลาดที่ละเอียดอ่อน: การล้างธงอย่างไม่ถูกต้องการเพิ่มประสิทธิภาพคอมไพเลอร์ที่ไม่ถูกต้องจากการขาดหายไปของสภาพการแข่งขันประสิทธิภาพการทำงานตามเวลาจริงของหมัดล้นกองซ้อน ฯลฯ เป็นต้นในกรณีที่ ISR ไม่ถูกต้อง เพิ่มขึ้นอีก มุ่งเน้นไปที่การเขียนโปรแกรมควบคุมบั๊กฟรีก่อนที่จะกังวลเกี่ยวกับสิ่งต่าง ๆ ที่น่าสนใจเช่นถ้าผู้เซทเทอร์ / ผู้ให้ทะลุทะลวงค่าใช้จ่ายเล็กน้อย
Lundin

10
การใช้ตัวแปรทั่วโลกนี้ขัดแย้งกับเมล็ดข้าวสำหรับฉัน

นี่เป็นปัญหาที่แท้จริง ได้รับมากกว่านั้น.

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

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

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

  1. ตัวนับสัญญาณนาฬิกาถูกจัดการโดยสัญญาณนาฬิกาของระบบ ฉันมักจะมีนาฬิกาขัดจังหวะเป็นระยะที่ทำงานทุก 1 มิลลิวินาที ซึ่งมักมีประโยชน์สำหรับช่วงเวลาต่าง ๆ ในระบบ วิธีหนึ่งในการดึงข้อมูลนี้ออกจากรูทีนการขัดจังหวะไปยังที่ที่เหลือของระบบสามารถใช้งานได้คือการรักษาตัวนับสัญญาณนาฬิกาทั่วโลก รูทีนการขัดจังหวะจะเพิ่มตัวนับทุกติ๊กนาฬิกา รหัสเบื้องหน้าสามารถอ่านตัวนับได้ตลอดเวลา บ่อยครั้งที่ฉันทำเช่นนี้เป็นเวลา 10 ms, 100 ms และแม้แต่ติ๊ก 1 วินาที

    ฉันแน่ใจว่าติ๊กขนาด 1 ms, 10 ms และ 100 ms มีขนาดเท่าคำที่สามารถอ่านได้ในการทำงานปรมาณูเดียว หากใช้ภาษาระดับสูงตรวจสอบให้แน่ใจว่าได้บอกคอมไพเลอร์ว่าตัวแปรเหล่านี้สามารถเปลี่ยนแปลงแบบอะซิงโครนัสได้ ใน C คุณประกาศให้พวกเขามีความผันผวนจากภายนอก แน่นอนว่านี่คือสิ่งที่จะเข้าสู่ไฟล์รวมของกระป๋องดังนั้นคุณไม่จำเป็นต้องจำไว้ว่าสำหรับทุกโครงการ

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

    แน่นอนว่าอาจมีการทำกิจวัตรเพื่อให้มีขนาดเล็กกว่า 1 ms, 10 ms ฯลฯ ทำเครื่องหมายที่เคาน์เตอร์ด้วย อย่างไรก็ตามนั่นเป็นสิ่งที่เล็กมากสำหรับคุณเพิ่มคำแนะนำจำนวนมากแทนที่การอ่านคำเดียวและใช้ตำแหน่งการโทรซ้อนอีกรายการหนึ่ง

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

  2. การกรองและปรับค่า A / D สุดท้าย สิ่งที่ต้องทำคือการมีการอ่านอินเตอร์รัปต์การจัดการรูทีนจาก A / D ฉันมักจะอ่านค่าอะนาล็อกได้เร็วกว่าที่จำเป็นจากนั้นใช้การกรองสัญญาณความถี่ต่ำ มักจะมีการปรับและชดเชยที่นำไปใช้

    ตัวอย่างเช่น A / D อาจอ่านเอาต์พุต 0 ถึง 3 V ของตัวแบ่งแรงดันไฟฟ้าเพื่อวัดอุปทาน 24 V การอ่านจำนวนมากถูกเรียกใช้ผ่านการกรองบางส่วนจากนั้นปรับสัดส่วนเพื่อให้ค่าสุดท้ายเป็นมิลลิโวลต์ หากอุปทานอยู่ที่ 24.015 V ค่าสุดท้ายคือ 24015

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

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


ฉันได้รับการบำบัดในสัปดาห์ที่ผ่านมาฉันพยายามที่จะสร้างรหัสของฉัน ฉันเห็นจุดของ Lundin เกี่ยวกับการ จำกัด การเข้าถึงตัวแปร แต่ฉันมองไปที่ระบบจริงของฉันและคิดว่ามันเป็นไปได้ที่จะเกิดจากระยะไกล ANY PERSON คนใดคนหนึ่งจะบิดเบือนตัวแปรสำคัญระดับโลกของระบบ ฟังก์ชั่น Getter / Setter ทำให้คุณต้องเสียค่าใช้จ่ายเมื่อเทียบกับการใช้ทั่วโลกและยอมรับว่าโปรแกรมเหล่านี้เป็นโปรแกรมที่ใช้งานง่าย ...
Leroy105

3
@ Leroy105 ปัญหาไม่ใช่ "ผู้ก่อการร้าย" โดยจงใจใช้ตัวแปรทั่วโลก มลภาวะ Namespace อาจเป็นปัญหาในโครงการขนาดใหญ่ แต่สามารถแก้ไขได้ด้วยการตั้งชื่อที่ดี ไม่ปัญหาที่แท้จริงคือโปรแกรมเมอร์ที่พยายามใช้ตัวแปรโกลบอลตามที่ตั้งใจไว้ แต่ไม่สามารถทำได้อย่างถูกต้อง อาจเป็นเพราะพวกเขาไม่ได้ตระหนักถึงปัญหาสภาพการแย่งชิงที่มีอยู่กับ ISR ทั้งหมดหรือเพราะพวกเขาสับสนกับการดำเนินการตามกลไกการป้องกันที่บังคับใช้หรือเพียงเพราะพวกเขาพ่นการใช้ตัวแปรโกลบอลทั่วทั้งรหัส รหัสที่อ่านไม่ได้
Lundin

คะแนนของคุณถูกต้องแลง แต่แม้ในตัวอย่างเหล่านี้การแทนที่extern int ticks10msด้วยinline int getTicks10ms()จะไม่มีความแตกต่างอย่างแน่นอนในชุดประกอบที่รวบรวมในขณะที่ในทางกลับกันมันจะทำให้ยากที่จะเปลี่ยนค่าในส่วนอื่น ๆ ของโปรแกรมโดยไม่ได้ตั้งใจ วิธีการ "ขอ" กับสายนี้ (เช่นการเยาะเย้ยเวลาระหว่างการทดสอบหน่วยการบันทึกการเข้าถึงตัวแปรนี้หรืออะไรก็ตาม) แม้ว่าคุณจะยืนยันว่าโอกาสที่ผู้เขียนโปรแกรมจะเปลี่ยนตัวแปรนี้ให้เป็นศูนย์ก็จะไม่มีค่าใช้จ่ายของผู้บุกรุกในบรรทัด
Groo

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

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

2

การขัดจังหวะใด ๆ โดยเฉพาะจะเป็นทรัพยากรระดับโลก อย่างไรก็ตามในบางครั้งมันอาจมีประโยชน์ที่จะให้อินเตอร์รัปต์หลายตัวใช้รหัสเดียวกัน ตัวอย่างเช่นระบบอาจมีหลาย UART ซึ่งทั้งหมดควรใช้ตรรกะการส่ง / รับที่คล้ายกัน

วิธีการที่ดีในการจัดการนั่นคือการวางสิ่งต่าง ๆ ที่ใช้โดยตัวจัดการขัดจังหวะหรือตัวชี้ไปที่พวกเขาในวัตถุโครงสร้างแล้วมีตัวจัดการการขัดจังหวะฮาร์ดแวร์ที่แท้จริงเป็นสิ่งที่ชอบ:

void UART1_handler(void) { uart_handler(&uart1_info); }
void UART2_handler(void) { uart_handler(&uart2_info); }
void UART3_handler(void) { uart_handler(&uart3_info); }

วัตถุuart1_info, uart2_infoฯลฯ จะเป็นตัวแปรทั่วโลก แต่พวกเขาจะเป็นเพียงตัวแปรทั่วโลกใช้โดยไสขัดจังหวะ ทุกอย่างอื่นที่ตัวจัดการกำลังจะสัมผัสจะได้รับการจัดการภายในที่

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

if (foo->timer)
  foo->timer--;

เขียน:

uint32_t was_timer;
was_timer = foo->timer;
if (was_timer)
{
  was_timer--;
  foo->timer = was_timer;
}

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


0

นี่คือแนวคิดสามข้อ:

ประกาศตัวแปร flag เป็นสแตติกเพื่อ จำกัด ขอบเขตเป็นไฟล์เดียว

ทำให้ตัวแปรแฟล็กเป็นไพรเวตและใช้ฟังก์ชัน getter และ setter เพื่อเข้าถึงค่าแฟล็ก

ใช้วัตถุการส่งสัญญาณเช่นสัญญาณแทนการตั้งค่าตัวแปร ISR จะตั้งค่า / โพสต์สัญญาณ


0

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

volatile bool *flag;  // must be initialized before the interrupt is enabled

ISR(...) {
    *flag = true;
}

หรือรหัสเชิงวัตถุด้วยฟังก์ชัน 'เสมือน':

HandlerObject *obj;

ISR(...) {
    obj->handler_function(obj);
}

... ขั้นตอนแรกต้องเกี่ยวข้องกับตัวแปรโกลบอลจริง (หรืออย่างน้อยคงที่) เพื่อเข้าถึงข้อมูลอื่น ๆ

กลไกทั้งหมดเหล่านี้เพิ่มทิศทางโดยอ้อมดังนั้นจึงมักไม่ทำหากคุณต้องการบีบรอบสุดท้ายออกจากตัวจัดการขัดจังหวะ


คุณควรประกาศสถานะเป็น int * ระเหย
next-

0

ฉันกำลังเขียนโปรแกรมสำหรับ Cortex M0 / M4 ในขณะนี้และวิธีการที่เราใช้ใน C ++ (ไม่มีแท็ก C ++ ดังนั้นคำตอบนี้อาจไม่เป็นหัวข้อ) คือ:

เราใช้คลาสCInterruptVectorTableที่มีรูทีนการบริการขัดจังหวะทั้งหมดซึ่งเก็บไว้ในเวกเตอร์อินเตอร์รัปต์จริงของคอนโทรลเลอร์:

#pragma location = ".intvec"
extern "C" const intvec_elem __vector_table[] =
{
  { .__ptr = __sfe( "CSTACK" ) },           // 0x00
  __iar_program_start,                      // 0x04

  CInterruptVectorTable::IsrNMI,            // 0x08
  CInterruptVectorTable::IsrHardFault,      // 0x0C
  //[...]
}

คลาสนี้CInterruptVectorTableใช้นามธรรมของเวกเตอร์ขัดจังหวะดังนั้นคุณสามารถผูกฟังก์ชันที่แตกต่างกับเวกเตอร์ขัดจังหวะในระหว่างรันไทม์

อินเทอร์เฟซของคลาสนั้นมีลักษณะดังนี้:

class CInterruptVectorTable  {
public :
    typedef void (*IsrCallbackfunction_t)(void);                      

    enum InterruptId_t {
        INTERRUPT_ID_NMI,
        INTERRUPT_ID_HARDFAULT,
        //[...]
    };

    typedef struct InterruptVectorTable_t {
        IsrCallbackfunction_t IsrNMI;
        IsrCallbackfunction_t IsrHardFault;
        //[...]
    } InterruptVectorTable_t;

    typedef InterruptVectorTable_t* PinterruptVectorTable_t;


public :
    CInterruptVectorTable(void);
    void SetIsrCallbackfunction(const InterruptId_t& interruptID, const IsrCallbackfunction_t& isrCallbackFunction);

private :

    static void IsrStandard(void);

public :
    static void IsrNMI(void);
    static void IsrHardFault(void);
    //[...]

private :

    volatile InterruptVectorTable_t virtualVectorTable;
    static volatile CInterruptVectorTable* pThis;
};

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


ตอนนี้ในโปรแกรมคุณสามารถใช้SetIsrCallbackfunctionเพื่อให้ตัวชี้ฟังก์ชั่นกับstaticฟังก์ชั่นที่จะถูกเรียกเมื่อเกิดขัดจังหวะ InterruptVectorTable_t virtualVectorTableชี้ถูกเก็บไว้ใน

และการใช้ฟังก์ชั่นอินเตอร์รัปต์มีลักษณะดังนี้:

void CInterruptVectorTable::IsrNMI(void) {
    pThis->virtualVectorTable.IsrNMI(); 
}

ดังนั้นจะเรียกใช้staticเมธอดของคลาสอื่น (ซึ่งอาจเป็นprivate) ซึ่งจากนั้นสามารถมีตัวstatic thisชี้อีกอันเพื่อเข้าถึงสมาชิกตัวแปรของวัตถุนั้น (เพียงอันเดียว)

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

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


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

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


1
"เพื่อให้คุณสามารถผูกฟังก์ชันที่แตกต่างกับเวกเตอร์ขัดจังหวะในระหว่างรันไทม์" นี่เป็นความคิดที่ไม่ดี "ความซับซ้อนของวงจร" ของโปรแกรมจะผ่านเข้าไปในหลังคา ชุดค่าผสมกรณีการใช้ทั้งหมดจะต้องได้รับการทดสอบเพื่อให้ไม่มีการกำหนดเวลาหรือความขัดแย้งในการใช้สแต็ก ปวดหัวมากสำหรับคุณลักษณะที่มีประโยชน์ จำกัด มาก IMO (ยกเว้นว่าคุณมีเคส bootloader นั่นเป็นอีกเรื่องหนึ่ง) โดยรวมแล้วนี่เป็นกลิ่นการเขียนโปรแกรมเมตา
Lundin

@Lundin ฉันไม่เห็นจุดของคุณจริงๆ เราใช้เพื่อผูกตัวอย่าง DMA interrupt เพื่อ SPI interrupt handler ถ้า DMA นั้นถูกใช้งานสำหรับ SPI และ UART interrupt handler ถ้ามันถูกใช้สำหรับ UART ตัวจัดการทั้งสองจะต้องมีการทดสอบแน่นอน แต่ไม่ใช่ปัญหา และมันก็ไม่มีอะไรเกี่ยวข้องกับการเขียนโปรแกรมเมตา
Arsenal

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

@Lundin ฉันคิดว่าเรามีมุมมองที่แตกต่างกันเกี่ยวกับเรื่องนี้เราสามารถเริ่มต้นการสนทนาเกี่ยวกับเรื่องนี้เพราะฉันยังไม่เห็นปัญหาของคุณกับมัน - ดังนั้นอาจเป็นคำตอบของฉันเขียนไม่ดีเลยว่าแนวคิดทั้งหมดเข้าใจผิด
Arsenal
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.