# 介面實驗四
## 工作日誌
9/29
用了一段時間,總算用出產波器產生sin波,經過adc轉換從建訊號,剩下差動

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}
$$

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 執行函式使用練習**

**量測單極性ADC線性誤差量測**

**雙通道弦波訊號擷取**

## 實驗結果
**C4MOS 執行函式使用練習**
>已把間隔時間用進去結構,但還是產生固定的波
**量測單極性ADC線性誤差量測**

$$
{\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

## 思考討論
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)