# 介面實驗四 ## 工作日誌 9/29 用了一段時間,總算用出產波器產生sin波,經過adc轉換從建訊號,剩下差動 ![](https://i.imgur.com/7xjo1It.jpg) 9/30 做完,剩下流程圖 ## 遭遇問題 ::: info 1. **量測單極性ADC線性誤差量測**中的LSB計算是依照參考電壓2.56V與10Bits的ADC量測去做計算嗎(像下面這樣)? $$ {\bf LSB}={工作電壓範圍 \over 幾位元}={2.56V \over 2^{10}}=0.0025V $$ 2. 我做出的${\bf Code_{0V}}$與${\bf Code_{Vbg}}$分別是${\bf 768}$與${\bf 487}$,計算出來的**零點偏差**為${\bf 768}$,而**增益**為${\bf -3*10^{-3}}$,想詢問這是否是在正確的範圍內,謝謝? $$ {\bf OFFSET}=Code_{0V}=768 $$ $$ {\bf GAIN}={1.23 \over {\bf Code_{Vbg}}-{\bf Code_{0V}}}({V \over LSB})={1.23 \over {\bf 487}-{\bf 768}}({V \over LSB})=-4.3*10^{-3} $$ ![](https://i.imgur.com/uIfcbQl.jpg) 3.想要詢問如何透過計時中斷配合降頻器完成多通道ADC的步驟來做雙通道弦波訊號擷取,因為看老師的講義看不懂該如何操作? ::: ## 程式碼 **C4MOS 執行函式使用練習** ::: spoiler ```c= #include "c4mlib.h" #include "math.h" #include "stdlib.h" #include "stdio.h" #include "tim.cfg" #include "adc.cfg" #include <avr/io.h> uint8_t num_1,num_2,num_3,num_4; uint16_t DATA = 54; uint8_t Time[4] = {1,2,3,4}; void Time_setup(); void Hi_set(void *x); void Low_set(void *x); FreqReduStr_t Fre; HWIntStr_t Tim; int main(void) { C4M_DEVICE_set(); int x = 0; REGFPT( &DDRF , 0x01 , 0 , 1 );//F0當作輸出 TIMHWINT_LAY( Tim2Int_str , 2 , 2 );//使用老師提供的函示建立結構體 FREQREDU_LAY( FreqRedu_str , 2 , 4 , 0 , 0 , Time );//使用老師提供的函示建立結構體 Fre = FreqRedu_str;//定義完後,宣告另一個結構體類型的參數代表此結構體 Tim = Tim2Int_str;//定義完後,宣告另一個結構體類型的參數代表此結構體 num_2 = FreqRedu_reg( &Fre , &Hi_set , &x , 1 , 0);//將HI功能方塊排入中斷除頻的排程表 num_3 = FreqRedu_reg( &Fre , &Low_set , &x , 1 , 1);//將LOW功能方塊排入中斷除頻的排程表 FreqRedu_en( &Fre , num_2 , 1 );//呼叫禁致能己登錄進中斷除頻中待執行工作方塊 FreqRedu_en( &Fre , num_3 , 1 );//呼叫禁致能己登錄進中斷除頻中待執行工作方塊 Time_setup();//設定timer while(1){} return 0; } void Time_setup(){ REGFPT( &TCCR2 , 0x48 , 3 , 1 );//波型模式選擇 REGFPT( &TCCR2 , 0x07 , 0 , 5 );//除頻值設定 REGFPT( &TCCR2 , 0x30 , 4 , 1 );//波行輸出選擇 REGFPT( &TIMSK , 0x80 , 7 , 1 );//中斷致能 REGPUT( &OCR2 , 1 , &DATA );//設定週期 sei(); } void Hi_set(void *x) { REGFPT( &PORTF , 0x01 , 0 , 1);//F0輸出HI } void Low_set(void *x) { REGFPT( &PORTF , 0x01 , 0 , 0);//F0輸出LOW } ISR(TIMER2_COMP_vect) //100HZ { FreqRedu_step(&Fre); } ``` ::: --- **量測單極性ADC線性誤差量測** 1. **C** ::: spoiler ```c= #include "c4mlib.h" #include "math.h" #include "stdlib.h" #include "stdio.h" uint8_t value_ADCL,value_ADCH,flag; uint16_t Code_0v,Code_vbg; float code[2][1]={}; void ADC_setup(); void ADC_getvalue(); int main(void) { C4M_DEVICE_set(); ADC_setup(); REGFPT( &ADMUX , 0x1F , 0 , 0x1F ); //輸入電壓0v ADC_getvalue(); //取值 //printf("ADIF_0v = %d\n",flag); //printf("ADCH=%d ADCL=%d\n",ADCH,ADCL); //個別顯示暫存器的值 看是否形式正確 Code_0v=(value_ADCH<<8)+value_ADCL; //存為10BITS的資料 REGFPT( &ADCSRA , 0x10 , 4 , 1 ); //消除中斷旗標 REGFPT( &ADMUX , 0x1F , 0 , 0x1E ); //輸入電壓1.23v _delay_ms(2); ADC_getvalue(); //取值 //printf("ADIF_0v = %d\n",flag); //printf("ADCH=%d ADCL=%d\n",ADCH,ADCL); //個別顯示暫存器的值 看是否形式正確 Code_vbg=(value_ADCH<<8)+value_ADCL; //存為10BITS的資料 REGFPT( &ADCSRA , 0x10 , 4 , 1 ); //消除中斷旗標 //printf("code_0v=%d code_vbg=%d\n",Code_0v,Code_vbg);//顯示0V與1.23V的轉換結果 code[0][0] = Code_0v; code[1][0] = Code_vbg; HMI_snput_matrix(8,2,1,code);//傳送矩陣給matlab return 0; } void ADC_setup() { REGFPT( &ADMUX , 0xC0 , 6 , 3 ); //設定參考電壓來源 REGFPT( &ADMUX , 0x20 , 5 , 0 ); //資料向右靠齊 REGFPT( &ADCSRA , 0x07 , 0 , 2 ); //設定工作時脈除頻 REGFPT( &ADCSRA , 0x20 , 5 , 0 ); //設定非連續轉換 REGFPT( &ADCSRA , 0x80 , 7 , 1 ); //設定ADC致能 REGFPT( &ADCSRA , 0x08 , 3 , 1 ); //設定禁制能中斷 REGFPT( &DDRF , 0x0F , 0 , 0 ); //設定ADC接角為輸入 } void ADC_getvalue() { REGFPT( &ADCSRA , 0x40 , 0 , 0x40 ); //觸發單一轉換 _delay_ms(2); //轉換需時 REGGET( &ADCL , 1 , &value_ADCL ); //先讀低BYTE資料 REGGET( &ADCH , 1 , &value_ADCH ); //後讀高BYTE資料,接著暫存器更新 REGFGT( &ADCSRA , 0x10 , 4 , &flag ); //讀取轉換旗標 } ``` ::: 2. **MATLAB** ::: spoiler ```matlab= clear; close all; clc; portnum=5; port=remo_open(portnum);%打開com5 [input,err_2] = remo_snget_matrix(port);%得到ASA電腦給的矩陣 remo_close(port);%關閉com5 input=single(input);%轉型 disp(input);%顯示outpute ``` ::: --- **雙通道弦波訊號擷取**只使用計時中斷與ADC 1. **C** :::spoiler ```c= #include "c4mlib.h" #include "math.h" #include "stdlib.h" #include "stdio.h" uint8_t value_ADCL,value_ADCH,flag; uint16_t Code_sing,Code_vbg,DATA = 54; float code[101][1] = {},tmp = 0,code_1[101][1] = {}; int msk = 0,num = 0,interupt_sing = 0,interupt_diff = 0; void ADC_setup(); void ADC_getvalue(); void Time_setup(); void ADC_save(); int main(void) { C4M_DEVICE_set(); ADC_setup(); REGFPT( &ADMUX , 0x1F , 0 , 0 );//輸入single_end_gnd_0 _delay_ms(2); Time_setup(); while(1) { if (msk == 100 && interupt_sing == 0) { HMI_snput_matrix(8,101,1,code);//傳送矩陣給matlab interupt_sing = 1; REGFPT( &ADMUX ,0x1F , 0 , 0x10);//輸入differential GAIN=1 _delay_ms(2); msk = 0; } if (msk == 100 && interupt_diff == 0 && interupt_sing == 1) { HMI_snput_matrix(8,101,1,code_1);//傳送矩陣給matlab interupt_diff = 1; break; } _delay_ms(1); } printf("the end\n"); return 0; } void ADC_setup() { REGFPT( &ADMUX , 0xC0 , 6 , 3 ); //設定參考電壓來源 REGFPT( &ADMUX , 0x20 , 5 , 0 ); //資料向右靠齊 REGFPT( &ADCSRA , 0x07 , 0 , 3 ); //設定工作時脈除頻 REGFPT( &ADCSRA , 0x20 , 5 , 0 ); //設定非連續轉換 REGFPT( &ADCSRA , 0x80 , 7 , 1 ); //設定ADC致能 REGFPT( &ADCSRA , 0x08 , 3 , 1 ); //設定禁制能中斷 REGFPT( &DDRF , 0x0F , 0 , 0 ); //設定ADC接角為輸入 } void Time_setup(){ REGFPT( &TCCR2 , 0x48 , 3 , 1 );//波型模式選擇 REGFPT( &TCCR2 , 0x07 , 0 , 5 );//除頻值設定 REGFPT( &TCCR2 , 0x30 , 4 , 1 );//波行輸出選擇 REGFPT( &TIMSK , 0x80 , 7 , 1 );//中斷致能 REGPUT( &OCR2 , 1 , &DATA );//設定週期 sei(); } void ADC_getvalue() { REGFPT( &ADCSRA , 0x40 , 0 , 0x40 ); //觸發單一轉換 while((ADCSRA&(1<<ADIF)) != (1<<ADIF)); //轉換需時 REGGET( &ADCL , 1 , &value_ADCL ); //先讀低BYTE資料 REGGET( &ADCH , 1 , &value_ADCH ); //後讀高BYTE資料,接著暫存器更新 REGFGT( &ADCSRA , 0x10 , 4 , &flag ); //讀取轉換旗標 } void ADC_save() { ADC_getvalue(); //取值 Code_sing=((value_ADCH<<8)+value_ADCL); //存為10BITS的資料 tmp = Code_sing*0.0025; REGFPT( &ADCSRA , 0x10 , 4 , 1 ); //消除中斷旗標 if (msk < 101 && interupt_sing == 0) { code[msk][0] = tmp; } if (msk < 101 && interupt_diff == 0 && interupt_sing ==1) { code_1[msk][0] = tmp; } } ISR(TIMER2_COMP_vect) //100HZ { msk++; ADC_save(); if (msk == 200) { msk = 199; } } ``` ::: 2. **MATLAB** :::spoiler ```matlab= clear; close all; clc; portnum=5; t=(0:0.0025:1)'; port=remo_open(portnum);%打開com5 [input,err] = remo_snget_matrix(port);%得到ASA電腦給的矩陣 [input_1,err_1] = remo_snget_matrix(port);%得到ASA電腦給的矩陣 remo_close(port);%關閉com5 input = single(input);%轉型 input_1 = single(input_1); subplot(2,1,1); plot(t(1:101),input(1:101)); xlabel('time(s)'); ylabel('voltage'); subplot(2,1,2); plot(t(1:101),input_1(1:101)) xlabel('time(s)'); ylabel('diff'); ``` ::: ## 流程圖 **C4MOS 執行函式使用練習** ![](https://i.imgur.com/dnMcrU8.jpg) **量測單極性ADC線性誤差量測** ![](https://i.imgur.com/xyJ1GM1.jpg) **雙通道弦波訊號擷取** ![](https://i.imgur.com/GnEDNFr.jpg) ## 實驗結果 **C4MOS 執行函式使用練習** >已把間隔時間用進去結構,但還是產生固定的波![](https://i.imgur.com/135r71f.jpg) **量測單極性ADC線性誤差量測** ![](https://i.imgur.com/sZwrLyi.jpg) $$ {\bf OFFSET}=Code_{0V}=768 $$ $$ {\bf GAIN}={1.23 \over {\bf Code_{Vbg}}-{\bf Code_{0V}}}({V \over LSB})={1.23 \over {\bf 487}-{\bf 768}}({V \over LSB})=-4.3*10^{-3} $$ **雙通道弦波訊號擷取**只使用計時中斷與ADC >取樣頻率為100HZ,上圖為單極性量測結果,下圖為差動結果,V+為分壓前的波,V-為分壓後的波,GAIN=1 ![](https://i.imgur.com/cskTtiY.jpg) ## 思考討論 1. 當初做出差動時,使用的是GAIN=10的方式發現只變成一條線,最後發現原因是因為放大後的電壓過大導致,只會在極限範圍上顯示一條直線。 2. 另外我原本在思考差動完sin波的交接處應該要為0,但經過思考過後,得出結論是此波有偏壓,(V+)-(V-)的結果只會剩下一半的偏壓,讓整個相減過後的結果都上升半個偏壓。 ## 驗收 用產波器產生隨機圖案的波形,大小在2.56之下,用adc讀完後以matlab顯示,c程式分成兩步,一步是觸發adc並讀回adc數值,另一步是送出資料給matlab,這兩步要登錄在除頻器上,要錄影,影片中要用三種波形測試 ### 程式碼 **c** :::spoiler ```c= #include "c4mlib.h" #include "math.h" #include "stdlib.h" #include "stdio.h" FreqReduStr_t Fre; HWIntStr_t Tim; uint8_t value_ADCL,value_ADCH,flag,num_1 ,num_2; uint16_t Code_sing,DATA = 54 , Time = 1; float code[101][1] = {},tmp = 0; int msk = 0,num = 0,interupt = 0; void ADC_setup(); void Time_setup(); void ADC_save(void *x); void matlab_transf(void *x); int main(void) { C4M_DEVICE_set(); ADC_setup(); printf("start\n"); REGFPT( &ADMUX , 0x1F , 0 , 0 );//輸入single_end_gnd_0 _delay_ms(2); int x = 0; TIMHWINT_LAY( Tim2Int_str , 2 , 2 );//使用老師提供的函示建立結構體 FREQREDU_LAY( FreqRedu_str , 2 , 2 , 0 , 0 , &Time );//使用老師提供的函示建立結構體 Fre = FreqRedu_str;//定義完後,宣告另一個結構體類型的參數代表此結構體 Tim = Tim2Int_str;//定義完後,宣告另一個結構體類型的參數代表此結構體 num_1 = FreqRedu_reg( &Fre , &ADC_save , &x , 1 , 0);//將觸發ADC轉換功能方塊排入中斷除頻的排程表 num_2 = FreqRedu_reg( &Fre , &matlab_transf , &x , 1 , 0);//將傳送matlab功能方塊排入中斷除頻的排程表 FreqRedu_en( &Fre , num_1 , 1 );//呼叫禁致能己登錄進中斷除頻中待執行工作方塊 FreqRedu_en( &Fre , num_2 , 1 );//呼叫禁致能己登錄進中斷除頻中待執行工作方塊 Time_setup();//設定TIMER while(interupt == 0){} return 0; } void ADC_setup() { REGFPT( &ADMUX , 0xC0 , 6 , 3 );//設定參考電壓來源 REGFPT( &ADMUX , 0x20 , 5 , 0 );//資料向右靠齊 REGFPT( &ADCSRA , 0x07 , 0 , 3 );//設定工作時脈除頻 REGFPT( &ADCSRA , 0x20 , 5 , 0 );//設定非連續轉換 REGFPT( &ADCSRA , 0x80 , 7 , 1 );//設定ADC致能 REGFPT( &ADCSRA , 0x08 , 3 , 1 );//設定禁制能中斷 REGFPT( &DDRF , 0x0F , 0 , 0 );//設定ADC接角為輸入 } void Time_setup() { REGFPT( &TCCR2 , 0x48 , 3 , 1 );//波型模式選擇 REGFPT( &TCCR2 , 0x07 , 0 , 5 );//除頻值設定 REGFPT( &TCCR2 , 0x30 , 4 , 1 );//波行輸出選擇 REGFPT( &TIMSK , 0x80 , 7 , 1 );//中斷致能 REGPUT( &OCR2 , 1 , &DATA );//設定週期 sei(); } void matlab_transf(void *x) { if (msk == 100 && interupt == 0) { HMI_snput_matrix(8,101,1,code);//傳送矩陣給matlab interupt = 1; } } void ADC_save(void *x) { msk++; REGFPT( &ADCSRA , 0x40 , 0 , 0x40 ); //觸發單一轉換 while((ADCSRA&(1<<ADIF)) != (1<<ADIF)); //轉換需時 REGGET( &ADCL , 1 , &value_ADCL ); //先讀低BYTE資料 REGGET( &ADCH , 1 , &value_ADCH ); //後讀高BYTE資料,接著暫存器更新 REGFGT( &ADCSRA , 0x10 , 4 , &flag ); //讀取轉換旗標 Code_sing=((value_ADCH<<8)+value_ADCL); //存為10BITS的資料 tmp = Code_sing*0.0025; REGFPT( &ADCSRA , 0x10 , 4 , 1 ); //消除中斷旗標 if (msk < 101) { code[msk][0] = tmp; } if (msk == 200) { msk = 199; } } ISR(TIMER2_COMP_vect) //100HZ { FreqRedu_step(&Fre); } ``` ::: **matlab** :::spoiler ```matlab = clear; close all; clc; portnum=5; t=(0:0.01:1)'; port=remo_open(portnum);%打開com5 [input_2] = remo_get_msg(port); [input,err] = remo_snget_matrix(port);%得到ASA電腦給的矩陣 remo_close(port);%關閉com5 input = single(input);%轉型 disp(input_2); disp(input); plot(t(1:101),input(1:101)); xlabel('time(s)'); ylabel('voltage'); ``` ::: ### 影片 [驗收影片](https://drive.google.com/file/d/15BTjl7MM5DI1WP2sO1FtoHMFqoNNgIIm/view?usp=sharing)