รีเลย์ที่เปิดใช้งานข้ามศูนย์


13

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

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

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

ปัญหาคือเวลา: ด้วย 50Hz AC เราได้รับ 100 ศูนย์ข้ามในวินาทีหนึ่งครึ่งรอบคือ 10ms เพื่อให้ได้ระยะทางที่เหมาะสมจากการข้ามศูนย์เพื่อให้ EMI ต่ำกล่าวว่าเราไม่ควรเปิดใช้งานเอาต์พุตมากกว่า 10% ที่ผ่านมา (หรือก่อนหน้านี้) เหตุการณ์การข้ามศูนย์ซึ่งหมายถึงความอดทน + -1ms นั่นไม่ได้หมายความว่าเวลาตอบสนอง 1ms - เราสามารถคาดหวังว่าการข้ามศูนย์ครั้งต่อไปจะเกิดขึ้นอย่างแม่นยำ 10ms หลังจากครั้งแรกหรือครั้งที่สี่ - 40ms มันเกี่ยวกับความละเอียด - ถ้าเราอนุญาตให้ทำปฏิกิริยา 20ms มันจะต้องอยู่ระหว่าง 19 ถึง 21ms ไม่ใช่ 18 หรือ 22

ฉันจะใช้งานตัวจับเวลาดังกล่าว - ทริกเกอร์เอาท์พุท GPIO ได้อย่างไรภายใน 1 มิลลิวินาทีเนื่องจากอินพุตตรวจจับขอบหรือภายในหลาย ๆ ค่าคงที่ 10ms ตั้งแต่นั้นมา - ควรมีค่าเผื่ออคติเชิงลบบางอย่าง (เช่นหม้อแปลงและรีเลย์ ดังนั้นฉันจึงต้องการให้ทริกเกอร์ปิด 8.4+ (n * 10) ms ตั้งแต่พัลซ์อินพุทด้วยวิธีการที่อคติตอบโต้การหน่วงเวลาที่วงจรแนะนำ) - แน่นอนว่า "ตามความต้องการของผู้ใช้" แน่นอนว่าผู้ใช้เขียน "1 "เป็นไฟล์ / sys / class / ... และในโอกาสที่ใกล้เคียงที่สุด (โดยประมาณ) เอาต์พุตจะเป็น" เปิด " ผู้ใช้เขียน "0" และเมื่อถึงศูนย์ข้ามมาถึงการถ่ายทอดรีเลย์ที่เฉพาะเจาะจง

ฉันเชื่อว่านี่จะต้องมีการเขียนหรือแฮ็คโมดูลเคอร์เนล คุณช่วยชี้ให้ฉันเห็นสิ่งที่จัดการกับพิน GPIO ของ Raspberry Pi ในเคอร์เนลและตัวจับเวลาชนิดใดที่ฉันสามารถแนบกับมันได้


โครงการที่น่าสนใจมากที่คุณพูดถึง! เพียงเพื่อให้ประมาณการคร่าวๆ: ฉันจะเชื่อมต่อกับคลื่นไซน์จากสัญญาณดั้งเดิม 50Hz ผ่านทาง schmitt trigger logic กับ GPIO จากนั้นจะสร้างสัญญาณขัดจังหวะบนขอบที่เพิ่มขึ้นหรือลดลงของสัญญาณตอนนี้คุณถูกล็อคที่ 50Hz AC และสามารถ 'ทำนาย' เมื่อการข้ามศูนย์ต่อไปจะเกิดขึ้น แน่นอนนี้เกี่ยวข้องกับการเขียนโปรแกรมไดรเวอร์เคอร์เนล Google เป็นเพื่อนของคุณ :-)
sparkie

ลองดูที่โมดูลหรี่ Inmojo ของ AC ฉันโคลนเรื่องนี้สำเร็จสำหรับโครงการโดยใช้เอกสารประกอบโอเพนซอร์ส นอกจากนี้ยังมีตัวอย่างโค้ด Arduino ที่อาจช่วยคุณผ่านตรรกะของรหัสของคุณ
Butters

@ ก้น: ฉันกลัวช่องว่างระหว่างการเขียนโปรแกรม Arduino และการเขียนโปรแกรม Linux Kernel module จะทำให้ซอฟต์แวร์ไม่มีประโยชน์สำหรับฉัน แต่แผนงานฮาร์ดแวร์ครอบคลุม 100% ของสิ่งที่ฉันต้องการ - ขอบคุณคุณบันทึกงานของฉันได้เล็กน้อย - ความแตกต่างระหว่างสองโครงการนั้นมีเฉพาะในซอฟต์แวร์เท่านั้น (เครื่องหรี่ไฟต้องสลับ triac cyclically ในขณะที่สวิตช์เปิด / ปิดเพียงครั้งเดียวต่อการสลับสวิตช์)
SF

... 100% ของด้านฮาร์ดแวร์ของหลักสูตร
เอสเอฟ

คำตอบ:


6

คุณไม่จำเป็นต้องแฮกเคอร์เนล คุณเพียงแค่ต้องย้ายกระบวนการออกจากคิวกำหนดการ

    #include<sched.h>

    struct sched_param param;               
    param.sched_priority = sched_get_priority_max(SCHED_FIFO);
    if( sched_setscheduler( 0, SCHED_FIFO, &param ) == -1 )
    {
            perror("sched_setscheduler");
            return -1;
    }

นับจากนี้ไปในกระบวนการของเราจะได้รับcat /proc/sys/kernel/sched_rt_runtime_usมิลลิวินาทีจากcat /proc/sys/kernel/sched_rt_period_usส่วนเวลาแต่ละมิลลิวินาทีของการดำเนินการอย่างต่อเนื่องโดยไม่เสี่ยงต่อการถูกจองไว้ล่วงหน้าในช่วงเวลานั้น (ในทางปฏิบัติโดยค่าเริ่มต้นบน BerryBoot: 0.95 วินาทีจากทุกวินาที) ด้วยค่าเหล่านี้ แต่ฉันไม่ต้องการอะไรมากสำหรับจุดประสงค์ของฉันที่นี่

ฉันใช้ฟังก์ชั่นจับเวลาเป็นมิลลิวินาที (นั่นเป็นเรื่องของความแม่นยำที่ฉันต้องการ) ขึ้นอยู่clock_gettime()กับนาฬิกาของฉัน

การโทรtimer(1)จะรีเซ็ตการโทรtimer(0)จะส่งคืนเวลานับตั้งแต่รีเซ็ต

    #include<time.h>
    typedef unsigned long long ulong64;

    ulong64 timer(unsigned char reset)
    {
            struct timespec t;
            static struct timespec lt={0,0};
            clock_gettime(CLOCK_REALTIME, &t);
            if(reset)
            {
                    lt.tv_sec = t.tv_sec;
                    lt.tv_nsec = t.tv_nsec;
            }

            int r = ((ulong64)(t.tv_sec - lt.tv_sec))*1000 + (t.tv_nsec - lt.tv_nsec)/1000000;

            return r;
    }

คุณต้องเชื่อมโยงกับrtไลบรารีเพื่อที่จะรวบรวม - เพิ่ม-lrtไปยังคำสั่ง gcc ของคุณ

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

    while(1)
    {
            //when idle, return a lot of CPU time back to the system. 
            //A call every 100ms is perfectly sufficient for responsive reaction.
            usleep(100000); 

            in  = bcm2835_gpio_lev(SWITCH_PIN);
            out = bcm2835_gpio_lev(TRIAC_PIN);

            if(in==out) continue;   //nothing to do; wait user input, return control to system.

            //The output needs to be changed.
            //First, let's wait for zero-crossing event.
            timer(TIMER_RESET);
            zx = bcm2835_gpio_lev(ZEROXING_PIN);

            //We don't want to freeze the system if the zero-xing input is broken.
            //If we don't get the event within reasonable time, 
            // (like three half-sines of the power; ZEROXING_TIMEOUT = 70)
            // we're going to bail.
            while(timer(TIMER_READ) < ZEROXING_TIMEOUT)
            {
                    if(zx != bcm2835_gpio_lev(ZEROXING_PIN))
                    {
                            //Event detected.                  
                            timer(TIMER_RESET);
                            break;
                    }
            }
            if(timer(TIMER_READ) >= ZEROXING_TIMEOUT) continue;     //Zero-crossing detection is broken, try again soon.

            //Now we are mere milliseconds after zero-crossing event arrived
            // (but it could have taken some time to arrive) so let's wait for the next one, making adjustments for the system delay.
            // This is to be worked out using an oscilloscope and trial and error.
            // In my case BIASED_DELAY = 19.

            while(timer(TIMER_READ)<BIASED_DELAY) ;

            //We can reasonably expect if we perform this right now:
            bcm2835_gpio_set_pud(TRIAC_PIN, in);
            //the signal will reach the output right on time.

            // The 100ms delay on return to start of the loop should be enough 
            // for the signals to stabilize, so no need for extra debouncing.
    }

จะใช้งานได้กับการใช้สวิตช์หรี่ที่ควบคุม pi สำหรับไฟหน้าหรือไม่? ผมคิดว่าผมจะต้อง 1) เปลี่ยนความละเอียดเพื่อสิ่งที่มีขนาดเล็กมาก (แทนของทุก 100ms) และ 2) แทนเพียงการตั้งค่าTRIAC_PINเพื่อinผมจะต้องตั้งค่าTRIAC_PINเป็น 1 รอจำนวนเงินที่กำหนดของเวลา (ตามสัดส่วนการ ต้องการระดับหรี่แสงที่ต้องการ) จากนั้นตั้งค่าTRIAC_PINกลับเป็น 0 จะใช้งานได้ไหม
rinogo

ฉันคิดว่าในวงหลักฉันยังต้องการเปลี่ยนสายif(in==out) continue;เป็นif(out==0) continue;ใช่มั้ย ที่จริงแล้วฉันยังใหม่กับการเขียนโปรแกรมสำหรับ pi ดังนั้นอาจไม่จำเป็น - ฉันเดาว่านี่เป็นสิ่งที่เกิดขึ้นพร้อมกัน (นั่นคือเราไม่ต้องกังวลกับลูปหลักที่ถูกเรียกในขณะที่ลูปซ้อนยังคงทำงานอยู่)
rinogo

(นี่คือทั้งหมดที่ใช้โมดูล Inmojo หรี่ดังกล่าวข้างต้นแน่นอน: inmojo.com/store/inmojo-market/item/ … )
rinogo

2
มีปัญหากับสิ่งนั้น สำหรับกิจกรรมของระบบที่เสถียรคุณต้องควบคุมระบบเป็นระยะและฉันสงสัยจริงๆว่าคุณจะกู้คืนได้ภายในเวลาอันสั้น (น้อยกว่า) 20ms ดังนั้นผลผลิตเหล่านี้จะส่งผลให้เกิดพัลส์ที่ไม่ได้รับและเป็นผลให้หลอดไฟกระพริบ ฉันถามคำถามเกี่ยวกับสิ่งนั้น แต่ไม่มีคำตอบ คุณอาจตั้งค่า sched_rt_runtime_us และ sched_rt_period_us เป็น -1 เพื่อปิดการใช้งานระบบ pre-emption ทั้งหมด แต่ถ้าคุณไม่ได้ sched_yield () หรือ usleep () เลยนั่นคือการสร้างปัญหา
เอสเอฟ

2
นั่นคือ: ด้วย SCHED_FIFO เมื่อคุณเริ่มการแบ่งเวลามันจะไม่ถูกขัดจังหวะจนกว่าคุณจะให้ความสมัครใจ (หรือ sched_rt_runtime_us ผ่านไป) แต่ระบบไม่รับประกันเมื่อคุณได้รับชิ้นส่วนนั้น ในกรณีของฉันฉันสังเกตเห็นว่าในการทำงานปกติเวลาระหว่างการโทร (การแบ่งส่วนเวลาให้กับงาน) สามารถขยายได้ถึง 0.1 วินาทีด้วยโหลดซีพียูสูงสุด บางทีช่วงเวลานั้นอาจปรับแต่งและบังคับให้สั้นลง แต่ฉันไม่รู้
เอสเอฟ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.