Arduino MEGA328P

Started by tha, September 10, 2017, 10:04:41 AM

Previous topic - Next topic

tha

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

tha

กำลังทบทวนอยู่ครับ ATmega328p ตัวนี้ ลองดูกราฟคุณสมบัติที่ผมโพสต์เอาไว้ อยู่ที่แถวที่ 13 ใช้ไฟได้ตั้งแต่ 1.8-5.5v เลยนี่ แต่สัญญานนาฬิกาก็ต้องลดลงตามส่วน(ดูกราฟคุณสมบัติ) คราวนี้ผมจะใช้ถ่านสองก้อน(3v)จ่ายไฟให้กับมัน คราวนี้คริสตัลที่ใช้บนบอร์ดมัน 16Mhz สิ สูงเกินไป มันต้องประมาณ 8Mhz ถึงจะพอดี ผมจะลองไม่เปลี่ยนคริสตัลเป็น 8Mhz แต่จะใช้ prescaler หารความถี่เอา คือหารด้วย 2 (ดูแถวที่ 11 หัวข้อ 13.11. System Clock Prescaler) คราวนี้ก็จะได้สัญญาน clock 8Mhz แล้ว
  แต่การใช้ไฟ 3v เราก็ต้องปรับ BOD ิbrown out detection ลงมาเป็น 2.7v ด้วย คือให้ต่ำกว่า 3v ปรับที่ fuse bit เลย ของผมตั้ง BOD เอาไว้ 4.3v ผมก็แก้เอาไว้ตั้งแต่บอร์ดยังใช้ไฟ 5v เลย



คราวนี้มาลองโปรแกรมดู(โปรแกรมไฟกระพริบ) ก็สามารถทำงานได้



ในโปรแกรม ก็กำหนด F_CPU = 8000000 Mhz เลย เพราะจะมีผลต่อฟังชั่น delay ในไฟล์ delay.h ที่เราใช้ในโฟลเดอร์ C:\WinAVR-20100110\avr\include\util\delay.h ลองไล่เช็คดู เช็คไฟล์พวกนี้ดูบ่อยๆแล้วก็จะชำนาญครับ

http://www.mediafire.com/file/fej1jp7db561wvs/blink_8M.rar/file

tha

รู้สึกมีพรายกระซิบบอกเรื่องให้กำหนด F_CPU เป็น 8000000Mhz เพราะมันมีผลกับฟังชั่น  _delay_ms(500); ที่เราใช้ด้วย ต้องขอขอบคุณท่านผู้รู้มา ณ ที่นี้ด้วยครับ


tha

มีพรายกระซิบบอกอีกละ ให้ใส่ NOP (no operation) หลังจากที่เราหาร prescaler แล้ว เพื่อให้สัญญานนาฬิกามัน stable ก่อนที่จะทำงานตามโปรแกรมต่อไป ผมก็เลยใส่ไป 4 NOP

Quote
int main(void)
{
   CLKPR = 0x80;          // set CLKPCE to 1 while set CLKPS to 0
   CLKPR = 0x01;          // set CLKPCE to 0 while set CLKPS to 1
                          // to prescaler with 2 to divide clock 16Mhz to 8Mhz
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );

   DDRD |= (1<<DDD7);     // port D7 are output
   PORTD &= ~(1<<PD7);     // port D7 low

tha

ลองเซท fuse ให้เป็น  Full Swing Crystal Oscillator ดู อันนี้ใช้สำหรับในที่ๆมีสัญญานรบกวนสูง หรือเอาขา XTAL2 เป็นเอาพุทไปขับอินพุทอื่นได้ใช่ใหม ดาต้าชีทเขาว่าอย่างนั้น



ทีนี้เราก็มาดูว่า Full Swing Crystal Oscillator (ดูโพสต์นี้แถวที่ 10 ประกอบนะ) ว่า บิท CKSEL[3:1] มีค่าเท่าไหร่ CKSEL[0] มีค่าเท่าไหร่ SUT[1:0] จะเอาค่าเท่าไหร่ดี ก็ดูในตาราง
          CKSEL[3:1] = 011
          CKSEL[0]    = 1           เลือกค่านี้เพราะเราใช้คริสตัลเป็นสัญญานนาฬิกา
          SUT[1:0]    = 11         เลือกค่านี้ถ้า power supply ของเรา เวลาเปิดเครื่องแล้วโวลท์ขึ้นมาช้า เราก็ตั้งเวลาเริ่มทำงานเผื่อเอาไว้ หรือจะเลือกเปิด brown out detection ตรวจจับว่าโวลท์ขึ้นมาสูงถึงระดับที่ตั้งไว้แล้วหรือยัง เราก็เซท  SUT[1:0]    = 01  ก็ได้

ส่วนบิท CKSEL[3:1] , CKSEL[0] , SUT[1:0] อยู่ตำแหน่งไหนใน fuse bits (fuse low byte) ก็ดูที่ตาราง fuse bits (ดูโพสต์นี้แถวที่ 13 ประกอบนะ) เราก็จะได้ค่า
           fuse low byte = 0xF7 = 0b1111 0111 ใช่ใหม

บิท 6 CKOUT เราก็ไม่โปรแกรม(ให้เป็น 1 บิทนี้จะกลับบิทเวลาไม่โปรแกรมก็ให้เป็น 1) เวลาโปรแกรม(ให้เป็น 0) ก็จะมีสัญญานนาฬิกาออกที่ขา CLKO (PB0) ดูเรื่อง 13.10. Clock Output Buffer แถวที่ 11 
                 
บิท 7 CKDIV8 เราก็ไม่โปรแกรม(ให้เป็น 1 บิทนี้จะกลับบิทเวลาไม่โปรแกรมก็ให้เป็น 1) เราก็ไม่หารสัญญานนาฬิกาด้วย 8

ส่วน high byte หรือ extended byte ก็ลองตรวจเช็คดูได้ แต่ตอนกด program ต้องระวังไม่ให้เอาบิท SPIEN ออก เพราะอันนี้จะเป็นตัวที่ติดต่อกับตัวโปรแกรม ET-AVR ISP mk-II ถ้าเอาออกก็จะติดต่อกับตัวโปรแกรมไม่ได้เลย

tha

คราวนี้เรามาลองเซท fuse bits ให้ใช้สัญญานนาฬิกาเป็น Calibrated Internal RC Oscillator ดูบ้างซึ่งทำให้ไม่ต้องใช้สัญญานนาฬิกาจากภายนอก(คริสตัลเป็นต้น) โดยปกติเจ้า AVR ATmega328p ตัวนี้เมื่อตอนที่ส่งออกมาจากโรงงานจะถูกตั้งค่า(default)เป็นยังไงดูที่ 13.2.1. Default Clock Source(แหล่งสัญญาณนาฬิกาเริ่มต้น) แถวที่ 10 ในโพสต์นี้

ทีนี้เราก็มาดูว่า Calibrated Internal RC Oscillator (ดูโพสต์นี้แถวที่ 11 ประกอบนะ) ว่า บิท CKSEL[3:0] มีค่าเท่าไหร่ SUT[1:0] จะเอาค่าเท่าไหร่ดี ก็ดูในตาราง
          CKSEL[3:0] = 0010
          SUT[1:0]    = 00        จะเลือกเปิด brown out detection enable ตรวจจับว่าโวลท์ขึ้นมาสูงถึงระดับที่ตั้งไว้แล้วหรือยัง จะตั้งไว้ 2.7v ตั้งได้ที่ fuse bits(ด้านบน) ก็จะได้ค่า extended fuse bits = 0xFD (ดูโพสต์นี้แถวที่ 13 ประกอบ)

ส่วนบิท CKSEL[3:0], SUT[1:0] อยู่ตำแหน่งไหนใน fuse bits (fuse low byte) ก็ดูที่ตาราง fuse bits (ดูโพสต์นี้แถวที่ 13 ประกอบนะ) เราก็จะได้ค่า
           fuse low byte = 0xC2 = 0b1100 0010 ใช่ใหม



คราวนี้เราก็กดโปรแกรมไป เสร็จแล้วก็มาบัดกรีถอดเอาคริสตัลออก ก็สามารถทำงานได้ด้วยถ่าน 2 ก้อน 3 โวลท์ โดยไม่ต้องใช้สัญญานนาฬิกาจากภายนอกเลย



ปล.กลับมาทำไมโครได้ ก็ชอบประดิษฐ์คิดค้นอยู่ ก็พอมีอะไรทำเพลินๆดี

tha

ตัวโปรแกรมก็ไม่มีอะไร ดัดแปลงแก้ใขของเก่านิดหน่อย
Quote
#define F_CPU 8000000UL    // set to 8000000Mhz because it effect to delay.h
#include <avr/io.h>        // The file is in C:\WinAVR-20100110\avr\include\avr
#include <util/delay.h>    // The file is in C:\WinAVR-20100110\avr\include\util\delay.h

int main(void)
{
   /*CLKPR = 0x80;          // set CLKPCE to 1 while set CLKPS to 0
   CLKPR = 0x01;          // set CLKPCE to 0 while set CLKPS to 1
                          // to prescaler with 2 to divide clock 16Mhz to 8Mhz
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );   */

   DDRD |= (1<<DDD7);     // port D7 are output
   PORTD &= ~(1<<PD7);     // port D7 low
   
   while(1)
   {
      PORTD ^= (1<<PD7);   // toggle PD7
     _delay_ms(500);
     //  PORTD |= (1<<PD7);     //  output high PD7
     //_delay_ms(250);
       //PORTD &= ~(1<<PD7);     //  output low PD7
     //_delay_ms(250);
   

     }
}


tha

ย้อนมาเรื่อง การหารความถี่สัญญานนาฬิกา(prescaler)สักหน่อย มันใช้เวลาในการเปลี่ยนจากความถี่เก่าไปเป็นความถี่ใหม่ T1+T2 ถึง T1+2*T2 ใช่ใหม (ดูเรื่อง 13.11. System Clock Prescaler แถวที่ 11)
                      T1 คือคาบเวลา 1 cycle สัญญานนาฬิกาความถี่เก่า = 1/f1 =1/16M
                      T2 คือคาบเวลา 1 cycle สัญญานนาฬิกาความถี่ใหม่ = 1/f2 =1/8M
T1 น้อยกว่า T2 อยู่แล้วใช่ใหม ก็ตีเป็นว่าการเปลี่ยนความถี่สัญญานนาฬิกาจะกินเวลา 3*T2 คือ 3 คาบเวลาใหม่ใช่ใหม
ดังนั้นเราก็จะไม่ให้ CPU มันไม่ทำอะไรก่อนสัก 3 คาบเวลา เผื่อไว้หนึ่งคาบเวลาเป็น 4 คาบเวลานะ ก็คือให้ CPU มันกระทำคำสั่ง nop (ไม่ทำงานอะไรเลย)สัก 4 คำสั่ง (คำสั่ง nop แต่ละคำสั่งใช้เวลา 1 cycle(1 คาบเวลา)) ก็เป็น 4 คาบเวลา ให้สัญญานนาฬิกาความถี่ใหม่มันมีความเสถียรก่อนที่จะปฏิบัติคำสั่งอื่นต่อไป ก็ใส่ไป 4 nop ก้ถูกแล้ว หรือจะให้แน่ก็ใส่เพิ่มก็ได้ จะเสียเวลาไปสักกี่ไมโครเสคกันหล่ะ

Quote
int main(void)
{
   CLKPR = 0x80;          // set CLKPCE to 1 while set CLKPS to 0
   CLKPR = 0x01;          // set CLKPCE to 0 while set CLKPS to 1
                          // to prescaler with 2 to divide clock 16Mhz to 8Mhz
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );
   asm volatile ("nop" :: );

   DDRD |= (1<<DDD7);     // port D7 are output
   PORTD &= ~(1<<PD7);     // port D7 low