---
# System prepended metadata

title: 介面實驗四

---

# 介面實驗四
## 工作日誌
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)