ใครช่วยอธิบายรหัสที่ดูแปลก ๆ นี้ใช้ในการตั้งค่าเครื่องจับเวลา


10

ในขณะที่ดูภาพร่างที่คนอื่นเขียนฉันอาจเจอโค้ดที่มีลักษณะดังนี้:

TCCR1A = 0;
TCCR1B = 0;

TCNT1 = 34286;
TCCR1B |= (1 << CS12);
TIMSK1 |= (1 << TOIE1);

ทั้งหมดที่ฉันรู้ก็คือมันมีบางอย่างที่เกี่ยวกับเวลา / ตัวจับเวลา (ฉันคิดว่า) ฉันจะถอดรหัส - และสร้าง - รหัสเช่นนี้ได้อย่างไร อะไรคือTCCR1A, TCCR1B, TCNT1, CS12, TIMSK1และTOIE1?


ไม่ทราบว่าพอที่จะตอบ แต่: electronics.stackexchange.com/questions/92350/... , forum.arduino.cc/index.php?topic=134602.0และstackoverflow.com/questions/9475482/... ไม่ทราบว่าคุณเคยเห็นสิ่งเหล่านี้แล้ว
เพนกวินที่ไม่เปิดเผยตัว

1
ดาวน์โหลดแผ่นข้อมูล "เสร็จสมบูรณ์" สำหรับอุปกรณ์ของคุณจากเว็บไซต์ Atmelและอ่านบทเกี่ยวกับตัวจับเวลา แผ่นข้อมูลเป็นสิ่งที่ดีที่จะอ่านในความคิดของฉัน
jippie

คำตอบ:


15

นี่ไม่ใช่รูปลักษณ์แปลก ๆ มันเป็นรหัส MCU ธรรมดาที่ดูเหมือนจริง

สิ่งที่คุณได้ที่นี่เป็นตัวอย่างของแนวคิดของการที่อุปกรณ์หน่วยความจำแมป โดยทั่วไปฮาร์ดแวร์ MCU มีที่ตั้งพิเศษในพื้นที่ที่อยู่ SRAM ของ MCU ที่กำหนดให้ ถ้าคุณเขียนไปยังที่อยู่เหล่านี้บิตไบต์ที่เขียนไปยังที่อยู่nควบคุมการทำงานของอุปกรณ์ต่อพ่วงเมตร

โดยทั่วไปหน่วยความจำของธนาคารบางแห่งจะมีสายเล็ก ๆ ที่ทำงานจากเซลล์ SRAM ไปยังฮาร์ดแวร์ หากคุณเขียน "1" ไปยังบิตนี้ในไบต์นั้นจะตั้งเซลล์ SRAM นี้เป็นลอจิคัลสูงซึ่งจะเปิดบางส่วนของฮาร์ดแวร์

หากคุณมองหาส่วนหัวของ MCU จะมีการแมปคำหลัก <-> ที่อยู่จำนวนมาก นี่คือวิธีที่สิ่งต่าง ๆ เช่นTCCR1Bฯลฯ ... ได้รับการแก้ไขในเวลารวบรวม

กลไกการแมปหน่วยความจำนี้ถูกใช้อย่างกว้างขวางใน MCU ATmega MCU ใน Arduino นั้นใช้เช่นเดียวกับ PIC, ARM, MSP430, STM32 และ STM8 MCU ซีรีส์รวมถึง MCU จำนวนมากที่ฉันไม่คุ้นเคยในทันที


รหัสArduinoเป็นของแปลก ๆ ที่มีฟังก์ชั่นที่เข้าถึงการควบคุม MCU ลงทะเบียนทางอ้อม แม้ว่านี่จะดู "ดีกว่า" แต่ก็ช้ากว่ามากและใช้พื้นที่โปรแกรมมากขึ้น

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

เลือกข้อความที่ตัดตอนมาจากแผ่นข้อมูลที่ลิงค์ด้านบน:

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นสำหรับตัวอย่างเช่นTIMSK1 |= (1 << TOIE1);ชุดบิตในTOIE1 TIMSK1นี่คือความสำเร็จโดยการขยับไบนารี 1 ( 0b00000001) ไปทางซ้ายทีละTOIE1บิตโดยมีTOIE1การกำหนดไว้ในไฟล์ส่วนหัวเป็น 0 จากนั้นค่าบิต ORed จะเป็นค่าปัจจุบันของTIMSK1ซึ่งตั้งค่าสูงหนึ่งบิตได้อย่างมีประสิทธิภาพ

ดูที่เอกสารสำหรับบิต 0 ของTIMSK1เราจะเห็นว่ามันถูกอธิบายว่า

เมื่อบิตนี้ถูกเขียนไปยังหนึ่งและตั้งค่าสถานะ I ในการลงทะเบียนสถานะ (ขัดจังหวะเปิดใช้งานทั่วโลก) การขัดจังหวะ Timer / Counter1 ล้นเปิดใช้งาน เวกเตอร์ขัดจังหวะที่เกี่ยวข้อง (ดู” การขัดจังหวะ” ในหน้า 57) จะถูกดำเนินการเมื่อตั้งค่าสถานะ TOV1 ซึ่งอยู่ใน TIFR1

บรรทัดอื่น ๆ ทั้งหมดควรตีความในลักษณะเดียวกัน


หมายเหตุบางส่วน:

TIMSK1 |= _BV(TOIE1);นอกจากนี้คุณยังอาจเห็นสิ่งที่ชอบ _BV()เป็นมาโครที่ใช้กันทั่วไปมีพื้นเพมาจากAVR libc การดำเนินงาน _BV(TOIE1)มีคุณสมบัติเหมือนกัน(1 << TOIE1)กับประโยชน์ของการอ่านที่ดีขึ้น

นอกจากนี้คุณยังอาจเห็นเส้นเช่น: หรือTIMSK1 &= ~(1 << TOIE1); TIMSK1 &= ~_BV(TOIE1);นี้มีฟังก์ชั่นตรงข้ามTIMSK1 |= _BV(TOIE1);ในการที่จะunsetsบิตในTOIE1 TIMSK1สิ่งนี้สามารถทำได้โดยการใช้ bit-mask ที่สร้างขึ้นโดย_BV(TOIE1)ดำเนินการกับ bitwise NOT operation บนมัน ( ~) จากนั้นTIMSK1ANDING ด้วยค่า NOTed นี้ (ซึ่งคือ 0b11111110)

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


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

uint8_t transactByteADC(uint8_t outByte)
{
    // Transfers one byte to the ADC, and receives one byte at the same time
    // does nothing with the chip-select
    // MSB first, data clocked on the rising edge

    uint8_t loopCnt;
    uint8_t retDat = 0;

    for (loopCnt = 0; loopCnt < 8; loopCnt++)
    {
        if (outByte & 0x80)         // if current bit is high
            PORTC |= _BV(ADC_MOSI);     // set data line
        else
            PORTC &= ~(_BV(ADC_MOSI));  // else unset it

        outByte <<= 1;              // and shift the output data over for the next iteration
        retDat <<= 1;               // shift over the data read back

        PORTC |= _BV(ADC_SCK);          // Set the clock high

        if (PINC & _BV(ADC_MISO))       // sample the input line
            retDat |= 0x01;         // and set the bit in the retval if the input is high

        PORTC &= ~(_BV(ADC_SCK));       // set clock low
    }
    return retDat;
}

PORTCคือรีจิสเตอร์ที่ควบคุมค่าพินเอาต์พุตภายในPORTCATmega328P PINCคือรีจิสเตอร์ซึ่งค่าอินพุตของPORTCพร้อมใช้งาน โดยพื้นฐานแล้วสิ่งต่างๆเช่นนี้เป็นสิ่งที่เกิดขึ้นภายในเมื่อคุณใช้งานdigitalWriteหรือdigitalReadฟังก์ชั่น อย่างไรก็ตามมีการดำเนินการค้นหาที่แปลง arduino "หมายเลขพิน" เป็นหมายเลขพินฮาร์ดแวร์ที่เกิดขึ้นจริงซึ่งใช้เวลาที่ไหนสักแห่งในอาณาจักรของรอบ 50 นาฬิกา ในขณะที่คุณสามารถเดาได้ว่าถ้าคุณกำลังพยายามที่จะไปอย่างรวดเร็วเสียเวลา 50 รอบในการดำเนินการที่ควรเพียงแค่ 1 เป็นบิตที่ไร้สาระ

ฟังก์ชั่นด้านบนอาจใช้เวลาสักครู่ในอาณาจักรของรอบนาฬิกา 100-200 รอบเพื่อถ่ายโอน 8 บิต สิ่งนี้มีผลต่อ 24 การเขียนพินและ 8 การอ่าน นี่คือเร็วขึ้นหลายเท่าแล้วใช้digital{stuff}ฟังก์ชั่น


โปรดทราบว่ารหัสนี้ควรทำงานกับ Atmega32u4 (ใช้ใน Leonardo) เนื่องจากมีตัวจับเวลามากกว่า ATmega328P
jfpoilpret

1
@Ricardo - อาจเป็น 90% + ของรหัสฝังตัวขนาดเล็กใช้การจัดการการลงทะเบียนโดยตรง การทำสิ่งต่าง ๆ ด้วยฟังก์ชั่นยูทิลิตี้ทางอ้อมไม่ใช่โหมดทั่วไปของการจัดการกับอุปกรณ์ต่อพ่วง / IO มีชุดเครื่องมือบางอย่างสำหรับการกำจัดการควบคุมฮาร์ดแวร์ (ตัวอย่างเช่น Atmel ASF) แต่โดยทั่วไปแล้วจะเขียนเพื่อรวบรวมมากที่สุดเท่าที่เป็นไปได้เพื่อลดค่าใช้จ่ายรันไทม์และเกือบจะต้องทำความเข้าใจกับอุปกรณ์ต่อพ่วงโดยการอ่านเอกสารข้อมูล
Connor Wolf

1
โดยทั่วไปสิ่งที่อาร์ดิโนโดยการพูดว่า "นี่คือฟังก์ชั่นที่ทำ X" โดยไม่ต้องกังวลกับการอ้างอิงเอกสารจริงหรือฮาร์ดแวร์ทำสิ่งที่มันทำอย่างไรไม่ใช่เรื่องปกติ ฉันเข้าใจว่ามันมีค่าเป็นเครื่องมือเบื้องต้น แต่ยกเว้นสำหรับการสร้างต้นแบบอย่างรวดเร็วมันไม่ได้ทำในสภาพแวดล้อมที่เป็นมืออาชีพจริงๆ
Connor Wolf

1
เพื่อให้ชัดเจนสิ่งที่ทำให้รหัส arduino ผิดปกติสำหรับเฟิร์มแวร์ MCU แบบฝังนั้นไม่ซ้ำกับรหัส arduino แต่เป็นหน้าที่ของวิธีการโดยรวม โดยทั่วไปเมื่อคุณมีความเข้าใจที่ดีเกี่ยวกับMCU จริงแล้วการทำสิ่งต่าง ๆ อย่างถูกต้อง (เช่นใช้การลงทะเบียนฮาร์ดแวร์โดยตรง) จะใช้เวลาไม่นาน เช่นถ้าคุณต้องการที่จะเรียนรู้ dev MCU จริงมันมากดีกว่าที่จะนั่งลงและเข้าใจในสิ่งที่ MCU ของคุณเป็นจริงทำแทนแล้วอาศัยคนอื่นเป็นนามธรรมซึ่งมีแนวโน้มที่จะรั่ว
Connor Wolf

1
โปรดทราบว่าฉันอาจดูถูกเหยียดหยามที่นี่ แต่พฤติกรรมที่ฉันเห็นในชุมชน arduino นั้นมีการตั้งโปรแกรมรูปแบบการต่อต้าน ฉันเห็นการเขียนโปรแกรม "copy-paste" จำนวนมากการใช้ไลบรารี่เป็นกล่องดำและการออกแบบทั่วไปที่ไม่ดีในชุมชนโดยรวม แน่นอนว่าฉันค่อนข้างกระตือรือร้นกับ EE.stackexchange ดังนั้นฉันอาจมีมุมมองที่ค่อนข้างเอียงเนื่องจากฉันมีเครื่องมือผู้ดูแลและมีคำถามปิดมากมาย มีอคติอย่างหนึ่งในคำถามของอาร์ดิโนที่ฉันเคยเห็นที่นั่นในทาง "บอกฉันหน่อยว่า C&P จะแก้ไขปัญหาอะไร" แทนที่จะทำเช่นนั้น "ทำไมจึงไม่ทำงาน"
Connor Wolf

3

TCCR1A คือตัวจับเวลา / นับ 1 ตัวควบคุมการลงทะเบียน A

TCCR1B คือตัวจับเวลา / นับ 1 ตัวควบคุมการลงทะเบียน B

TCNT1 คือค่าตัวนับของตัวจับเวลา / ตัวนับ 1

CS12 เป็นบิตเลือกนาฬิกาที่ 3 สำหรับตัวจับเวลา / ตัวนับ 1

TIMSK1 เป็นการลงทะเบียนหน้ากากอินเตอร์รัปต์ของตัวจับเวลา / นับ 1

TOIE1 คือการเปิดใช้งานการขัดจังหวะตัวจับเวลา / ตัวนับ 1 ล้น

ดังนั้นรหัสจะเปิดใช้งานตัวจับเวลา / ตัวนับ 1 ที่ 62.5 kHz และตั้งค่าเป็น 34286 จากนั้นจะเปิดใช้งานการขัดจังหวะการโอเวอร์โฟลว์ดังนั้นเมื่อถึง 65535 มันจะเรียกใช้ฟังก์ชั่นการขัดจังหวะ ISR(timer0_overflow_vect)


1

CS12 มีค่าเป็น 2 เนื่องจากมันหมายถึงบิต 2 ของการลงทะเบียน TCCR1B

(1 << CS12) รับค่า 1 (0b00000001) และเลื่อนไปทางซ้าย 2 ครั้งเพื่อรับ (0b00000100) ลำดับการดำเนินการกำหนดว่าสิ่งต่าง ๆ ใน () เกิดขึ้นก่อนดังนั้นสิ่งนี้จะเกิดขึ้นก่อนที่จะมีการประเมิน "| ="

(1 << CS10) รับค่า 1 (0b00000001) และเลื่อนไปทางซ้าย 0 ครั้งเพื่อรับ (0b00000001) ลำดับการดำเนินการกำหนดว่าสิ่งต่าง ๆ ใน () เกิดขึ้นก่อนดังนั้นสิ่งนี้จะเกิดขึ้นก่อนที่จะมีการประเมิน "| ="

ดังนั้นตอนนี้เราได้รับ TCCR1B | = 0b00000101 ซึ่งเหมือนกับ TCCR1B = TCCR1B | 0b00000101

ตั้งแต่ "|" คือ "OR" บิตทั้งหมดนอกเหนือจาก CS12 ใน TCCR1B จะไม่ได้รับผลกระทบ

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