Analog Read()
จะทำการอ่านค่า analog ของ 8051 ซึ่งน่าจะเทียบได้กับ AnalogRead() ของ Arduino ขั้นตอนก็มากหน่อย ทำตามคู่มือ พอลองจนได้ที่แล้ว ก็เอามาเขียนไว้กันลืม
MCU ที่ใช้คือรุ่น STC8G1K08A(8PIN) ใน STC8G Series ตามคู่มือจะมี ADC0 - ADC5 ขนาด 10bit (2^10 = 1024 ค่าสูงสุดเท่ากับ 1023) แต่ไม่มี Enhanced PWM (แต่ก็ทำได้โดย PCA)
ขาไหนเป็นขาไหน? ทุกขาเป็น ADC ทั้งหมด มีการใช้งานอยู่ port3,5 ดังนี้
ADC0 P3.0
ADC1 P3.1
ADC2 P3.2
ADC3 P3.3
ADC4 P5.4
ADC5 P5.5
จะเอาขาไหนไปทำ analog input ต้องกำหนดให้ขานั้นเป็น high-impedance input mode (โดยกำหนดในรีจิสเตอร์ PxP1)
Register ที่เกี่ยวข้อง (ก็ไม่เหมือนกับ STC15W408AS ซะทั้งหมด ที่ใช้รีจิสเตอร์ P1ASF ในการกำหนดช่องสัญญาณ จึงต้องอ้างอิงจากคู่มือตลอด)
ADC_CNTR ,control
ADCCFG ,config
ADC_RES ,result
ADC_RESL ,result low
ADCTIM ,time
P_SW2 ,access bit
P3M0 ,pin mode
P3M1
P5M0
P5M1
การแปลงค่าจะทำทีละช่องสัญญาณ เริ่มจาก1.)กำหนดช่อง จากนั้น2.)จึงเริ่มการแปลง 3.)พอแปลงเสร็จ เอาค่าไปใช้ 4.)แล้วก็เปลี่ยนช่องสัญญาณถัดไป แบบนี้วนๆไป และจะต้องใช้ adc interrupt ด้วย
กำหนดเป็นฟังก์ชั่นก็สะดวกดี
void adcInit(){
P3M1 |= 0x08; //P33 is high impedance,ADC3
P5M1 |= 0x10; //P54 is high impedance,ADC4
P_SW2 |= 0x80; //access bit ADCTIM
ADCTIM = 0x3f; //recommenced by manual
P_SW2 &= 0x7f; //stop access
ADCCFG = 0x2b; //Result align right, speed 1011;sys/2/12
ADC_CONTR = 0x83; //Enable module on ADC3
EADC = 1; //ADC interrupt enable
EA = 1; //interrupt enable
ADC_CONTR |= 0x40; //Start ADC
delay(2); //For stability conversion result
}
เมื่อมันแปลงค่าเสร็จแล้ว flag ของการแปลงค่าอยู่ที่ bit5 ของ register ADC_CONTR และจะมี logic 1 ค่าของการแปลงก็อยู่ใน ADC_RES, ADC_RESL
ใน ISR เราต้องทำการ reset flag อีกเรื่องเพื่อความสะดวก ก็เอาค่าใน ADC_RES,ADC_RESL มาเก็บไว้ในตัวแปรแยกไว้ต่างหาก เรียกใช้ง่ายไม่งง
void adcisr() interrupt 5
{
ADC_CONTR &= ~ 0x20; //reset flag , bit5 ของ ADC_CONTR ต้องมี logic 0
if(ADC_CONTR -0x83 == 0){ //ถ้าเป็น ADC3
adc3_dat = (ADC_RESL) | (ADC_RES<<8);
ADC_CONTR = 0x84; //เปลี่ยนเป็น ADC4
}else{ //ถ้าไม่ใช่ ADC3
adc4_dat = (ADC_RESL) | (ADC_RES<<8);
ADC_CONTR = 0x83; //เปลี่ยนเป็น ADC3
}
ADC_CONTR |= 0x40; //start ADC again
}//end isr
ถ้าใช้หลาย ADC ก็หาวิธีเปลี่ยนช่องสัญญาณได้ตามสะดวก อย่างเช่น switch case
ในลูปปกติ ก็เอาค่าในตัวแปร adc3_dat, adc4_dat ไปใช้งานได้ตามต้องการ เป็นข้อมูล 10bit ที่ถูกเก็บไว้ในตัวแปรขนาด 16bit
ตามคลิปนี้ เอา output จากเซนเซอร์เข้า P3.3 ส่วน P5.4 ปล่อยลอยๆ ไว้ วัดค่าได้แล้วก็ส่งไปให้ HMI แสดงผลออกมาเป็นตัวเลข
delay function ที่ทำไว้ใช้ กำหนดไว้ประมาณนี้ ก็ไม่ได้แม่นยำเป๊ะ พอใช้งานได้ ใช้กับ FOSC 18.432MHz
void delay(u16 t){
int i, j;
for (i = 0; i <t ; i++)
for (j = 0; j <2616; j++); //base on 18.432MHz
}//end delay
ลองใช้คำสั่ง delay(5) ได้มาประมาณนี้
Fig. 1 Delay 5ms |
Analog Write()
ทำ PWM ด้วย PCA hi speed output mode
Fig.2 CCP0, CCP1, CCP2 pin |
จะใช้ CCAPM0 ในการทำตัวจับเวลา มีรีจิสเตอร์ที่เกี่ยวข้องตามนี้
CCAPM0, CCON, CMOD, CL, CH, CCAP0L, CCAP0H, CCF0 และ CR
เมื่อเริ่มต้นจับเวลา ต้องทำการกำหนดค่าเปรียบเทียบให้กับ CCAP0L, CCAP0H เมื่อ mcu ทำงานแต่ละรอบจะเก็บค่าไว้ใน CH, CL จนมันเท่ากันกับตัวเปรียบเทียบ เมื่อนั้นจะเกิดการ interrupt ขึ้น จากนั้นเราก็กำหนดค่าให้กับตัวเปรียบเทียบอีก เป็นแบบนี้วนๆ ไป เราต้องรู้ว่าแต่ละรอบของ machine cycle นั้นกินเวลาเท่าใด มันคือ 12T หมายถึง 1 machine cycle และก็ใช้สัญญาณนาฬิกา 12 ลูก
เนื่องจากเป็นระบบ 12T มันก็ใช้ความถี่ FOSC / 12 ตัวอย่างเช่น ระบบกำหนดสัญญาณที่ 18.432MHz
18,432,000 Hz / 12 = 1,536,000 Hz
คาบเวลาดังกล่าว มันเป็นส่วนกลับของความถี่ นั่นคือ
1 / 1,536,000 = 0.65104us
0.65104us คือ เวลาที่ใช้ทำงาน 1 cycle (12 clock pulse)
แล้ว 1ms มันต้องทำงานกี่รอบกัน?
1 รอบการทำงานใช้เวลา 0.65104us
ทำ x รอบ จะได้เวลา 1ms (1ms มันเท่ากับ 1000us)
x/1 = 1000us / 0.65104us
x = 1536 รอบ
สรุปว่าต้องให้ mcu ทำงาน 1536 รอบจะใช้เวเลา 1ms
ค่า 1536 (600H) นี้จะต้องถูกกำหนดให้กับรีจิสเตอร์ CCAP0L, CCAP0H ในกรณีที่ต้องการจับเวลา 1ms
และต้องเข้าใจว่า ว่าเป็น 16bit timer นั่นหมายถึงจำนวนรอบการทำงานไม่เกิน 65535
ส่วนการกำหนดค่าทำดังนี้
CCAP0L = 0x600;
CCAP0H = 0x600>>8;
Arduino จะมีความถี่ของ PWM ราวๆ 490Hz นั่นคือมันจะมีคาบเวลาของ 1 pulse อยู่ที่ 1/490 = 2.04 ms ( 1 ลูกคลื่นใช้เวลา 2.04 มิลลิวินาที)
ถ้าเราทำ PWM โดยที่ 1 ลูกคลื่นมีคาบเวลา 1ms จะได้ว่า PWM นั้นมีความถี่ 1/0.001s = 1000Hz
การสั่งให้ขา P32 เป็น analog output (โดยการทำ PWM) ทำประมาณนี้
void main(){
CCON = 0x00;
CMOD = 0x00;
CL = 0x00;
CH = 0x00;
CCAPM0 = 0x4d; //ใช้โมดูล 0 และเป็น hi speed output
t = 768; // 1536 / 2
CCAP0L = t;
CCAP0H = t>>8;
t += 768;
CR = 1; //start capture
EA = 1; //enable global interrupt
while();
}
ถ้าไม่ทำไรเพิ่ม
เราก็จะได้ PWM 50%duty เราก็ควรจัดการปรับเวลาของการเกิด logic 1 , logic
0 เพื่อให้ได้ %duty ตามต้องการ จะต้องทำใน interrupt service routine
(ISR)
เราก็ดูว่าเมื่อไหร่ที่มันมี logic 0 เราก็ให้เป็น logic 0 นานเท่าที่เราต้องการ
และที่สำคัญคือ ผลรวมของเวลาที่เป็น logic 0 และ logic 1 เมื่อรวมกันต้องได้ 1ms (ตามตัวอย่างได้ 1536) เพราะจะได้ความที่ 1000Hz
void pca_isr() interrupt 7 {
CCF0 = 0; //clear flag
CR = 0; //stop
if(P32 == 0)
{
t += 1536*0.05; // (1536*0.05 = 77) มันจะเป็น logic 0 เท่ากับ 5/100 ms
CCAP0L = t;
CCAP0H = t>>8;
} else
{
t += 1536*0.95; // (1536*0.95 = 1459) มันจะเป็น logic 1 เท่ากับ 95/100 ms
CCAP0L = t;
CCAP0H = t>>8;
}
CR = 1; //start
}
Fig.3 PCA making PWM on P32 |
เราตั้งเวลาตอนมันเป็น logic 0 ให้มีค่า 5% ของ 1ms (จำนวน 77 รอบ) จากนั้นพอ mcu ทำงานครบ 77 รอบ จะเกิด interrupt อีกครั้ง เนื่องจากมันอยู่ในโหมด hi speed output (CCAPM0 = 0x4d) รอบนี้มันจะมี logic 1 ซึ่งเราได้กำหนดให้มันต้องทำงานอีก 1459 รอบ เมื่อครบแล้วก็เกิด interrupt รอบใหม่อีก พร้อมกับมี logic 0 วนๆ ไป
โปรดสังเกตุว่ามันเกิด interrupt 2 ครั้ง ในช่วงเวลา 1 ms ถ้าเราเอาตัวแปรนับจำนวนมาใส่ใน ISR เราก็ได้ timer 1ms ไว้ใช้งาน หรือว่าจะใช้ module1, module2 แยกต่างหากก็ทำได้ โดยให้รีจิสเตอร์ CCAPM1, CCAP1L,CCAP1H สำหรับ module1 หรือจะเป็น CCAPM2, CCAP2L,CCAP2H สำหรับ module2
ลองปรับค่า%duty ให้มันเพิ่ม-ลด ก็ยากต่อการควบคุมเหมือนกัน บางจังหวะความถี่มันผิดปกติมีการกระพริบจนสังเกตุเห็นได้
ก็ตามนั้น
ไม่มีความคิดเห็น:
แสดงความคิดเห็น