# 介面實驗五
## 工作日誌
10/11
使用MCP4921 IC輸出3V的電壓
10/12
要產生正弦波,卻無法用出來,示波器的波型很奇怪,最後詢問發現我不小心對IC輸出負值,所以才無法產生弦波
10/13
成功用示波器看到弦波
10/14
成功用DAC輸出正弦波的電壓,透過ADC讀取產生圖
## 程式碼
**DAC介面卡原型試作**-使用MCP4921 DAC輸出3V電壓,電表觀測
C語言
:::spoiler
```c=
#include "c4mlib.h"
#include "math.h"
#include "stdlib.h"
#include "stdio.h"
#include <avr/io.h>
#include "spi.cfg"
void SPI_setup();
void pin_setup();
#define PIN_CS 0
#define PIN_F1CS 1
#define PIN_SCK 1
#define PIN_MOSI 2
#define PIN_MISO 3
uint16_t data = 20480+3146;//20480+3072=23552
uint16_t data_1 =3146;
int main(void)
{
C4M_DEVICE_set();
pin_setup();
SPI_setup();// initial SPI
REGFPT( &PORTD , 0x03 , 0 , 3 );
while(1)
{
REGFPT(&PORTD,1,0,0);//set CS low before receive data
ASA_SPIM_trm( 4 , 0 , 0 , 2 , &data , 10);//對A通達雙倍參考電壓輸出
REGFPT(&PORTD,1,0,1);//set CS high after receive data
REGFPT(&PORTD,2,0,0);//set LDAC low
_delay_ms(100);
REGFPT(&PORTD,2,0,2);//set LDAC high
}
return 0;
}
void SPI_setup()
{
SPCR=(1<<SPE)|(1<<MSTR);// enable SPI,set master mode,CPHA=0,CPOL=0,DORD=0,SPR0:1=0
REGFPT(&SPSR,0x01,0,1);//set double mode
}
void pin_setup()
{
DDRB |= (1<<PIN_SCK)|(1<<PIN_MOSI)|(1<<PIN_CS);//set pin CS,SCK as output
DDRB &= ~(1<<PIN_MISO);// set pin MISO as input
DDRD |= (1<<PIN_CS)|(1<<PIN_F1CS)|(1<<5)|(1<<6)|(1<<7);//set D0 D1 as input
}
```
:::
---
**示波器觀察DAC輸出波型**
C語言
:::spoiler
```c=
#include "c4mlib.h"
#include "math.h"
#include "stdlib.h"
#include "stdio.h"
#include <avr/io.h>
#include "spi.cfg"
#include "tim.cfg"
void SPI_setup();
void pin_setup();
void Time_setup();
void DAC_put();
#define PIN_CS 0
#define PIN_F1CS 1
#define PIN_SCK 1
#define PIN_MOSI 2
#define PIN_MISO 3
int i =0,b=0;
uint16_t data = 20480+3146,DATA= 86,xx;//20480+3072
float x[101][1] = {};
float data_1=20480,data_2=1024;
int main(void)
{
C4M_DEVICE_set();
HMI_snget_matrix(8,101,1,x);
pin_setup();
SPI_setup();// initial SPI
REGFPT( &PORTD , 0x03 , 0 , 3 );
for (int j =0;j<=100;j++)
{
x[j][0]=x[j][0]*data_2;
}
HMI_snput_matrix(8,101,1,x);//傳送矩陣給matlab
for (int j =0;j<=100;j++)
{
x[j][0]=data_1+x[j][0];
}
Time_setup();
while(1){}
return 0;
}
void SPI_setup()
{
SPCR=(1<<SPE)|(1<<MSTR);// enable SPI,set master mode,CPHA=0,CPOL=0,DORD=0,SPR0:1=0
REGFPT(&SPSR,0x01,0,1);//set double mode
}
void pin_setup()
{
DDRB |= (1<<PIN_SCK)|(1<<PIN_MOSI)|(1<<PIN_CS);//set pin CS,SCK as output
DDRB &= ~(1<<PIN_MISO);// set pin MISO as input
DDRD |= (1<<PIN_CS)|(1<<PIN_F1CS)|(1<<5)|(1<<6)|(1<<7);//set F0 F1 as input
}
void Time_setup()
{
REGFPT( &TCCR2 , 0x48 , 3 , 1 );//波型模式選擇
REGFPT( &TCCR2 , 0x07 , 0 , 4 );//除頻值設定
REGFPT( &TCCR2 , 0x30 , 4 , 1 );//波行輸出選擇
REGFPT( &TIMSK , 0x80 , 7 , 1 );//中斷致能
REGPUT( &OCR2 , 1 , &DATA );//設定週期
sei();
}
void DAC_put()
{
xx=x[i][0];
REGFPT(&PORTD,1,0,0);//set CS low before receive data
ASA_SPIM_trm( 4 , 0 , 0x50 , 2 , &xx , 1);//對A通達雙倍參考電壓輸出
REGFPT(&PORTD,1,0,1);//set CS high after receive data
REGFPT(&PORTD,2,0,0);//set LDAC low
_delay_us(1);
REGFPT(&PORTD,2,0,2);//set LDAC high
i++;
if (i == 100)
{
i = 0;
}
}
ISR(TIMER2_COMP_vect)
{
DAC_put();
}
```
:::
MATLAB
:::spoiler
```matlab=
clear;
close all;
clc;
portnum=5;
t=(0:0.01:1)';
p=sin(2*pi*5*t)+1.5;
%p(1:101)=3;
x=p(1:101);
x=single(x);
port=remo_open(portnum);%打開com5
[err]=remo_snput_matrix(port,x);
[input,err_2] = remo_snget_matrix(port);%得到ASA電腦給的矩陣
remo_close(port);%關閉com5
input=single(input);%轉型
%disp(input_2);%顯示outpute
plot(t(1:101),input(1:101),'r');
%legend(input_2)
```
:::
---
**ADC觀察DAC輸出波型**
C語言
:::spoiler
```c=
#include "c4mlib.h"
#include "math.h"
#include "stdlib.h"
#include "stdio.h"
#include <avr/io.h>
#include "spi.cfg"
#include "tim.cfg"
void SPI_setup();
void pin_setup();
void Time_setup();
void DAC_put();
void ADC_setup();
void ADC_save();
#define PIN_CS 0
#define PIN_F1CS 1
#define PIN_SCK 1
#define PIN_MOSI 2
#define PIN_MISO 3
int i =0,msk = 0,num = 0,interupt = 0,j=0;
uint8_t value_ADCL,value_ADCH,flag;
uint16_t data = 20480+3146,DATA= 86,xx,Code_sing,Code_vbg;//20480+3072
float x[101][1] = {},code[101][1] = {},tmp = 0;
float data_1=20480,data_2=1024;
int main(void)
{
C4M_DEVICE_set();
HMI_snget_matrix(8,101,1,x);
ADC_setup();
REGFPT( &ADMUX , 0x1F , 0 , 0x00 );//輸入
_delay_ms(2);
pin_setup();
SPI_setup();// initial SPI
REGFPT( &PORTD , 0x03 , 0 , 3 );
for (int j =0;j<=100;j++)
{
x[j][0]=x[j][0]*data_2;
}
//HMI_snput_matrix(8,101,1,x);//傳送矩陣給matlab
for (int j =0;j<=100;j++)
{
x[j][0]=data_1+x[j][0];
}
Time_setup();
while(1)
{
if (msk == 100 && interupt == 0)
{
HMI_snput_matrix(8,101,1,code);//傳送矩陣給matlab
interupt = 1;
break;
}
_delay_us(1);
}
return 0;
}
void SPI_setup()
{
SPCR=(1<<SPE)|(1<<MSTR);// enable SPI,set master mode,CPHA=0,CPOL=0,DORD=0,SPR0:1=0
REGFPT(&SPSR,0x01,0,1);//set double mode
}
void pin_setup()
{
DDRB |= (1<<PIN_SCK)|(1<<PIN_MOSI)|(1<<PIN_CS);//set pin CS,SCK as output
DDRB &= ~(1<<PIN_MISO);// set pin MISO as input
DDRD |= (1<<PIN_CS)|(1<<PIN_F1CS)|(1<<5)|(1<<6)|(1<<7);//set F0 F1 as input
}
void Time_setup(){
REGFPT( &TCCR2 , 0x48 , 3 , 1 );//波型模式選擇
REGFPT( &TCCR2 , 0x07 , 0 , 3 );//除頻值設定
REGFPT( &TCCR2 , 0x30 , 4 , 1 );//波行輸出選擇
REGFPT( &TIMSK , 0x80 , 7 , 1 );//中斷致能
REGPUT( &OCR2 , 1 , &DATA );//設定週期
sei();
}
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 DAC_put()
{
xx=x[i][0];
REGFPT(&PORTD,1,0,0);//set CS low before receive data
ASA_SPIM_trm( 4 , 0 , 0x50 , 2 , &xx , 1);//對A通達雙倍參考電壓輸出
REGFPT(&PORTD,1,0,1);//set CS high after receive data
REGFPT(&PORTD,2,0,0);//set LDAC low
_delay_us(1);
REGFPT(&PORTD,2,0,2);//set LDAC high
i++;
if (i == 100)
{
i = 0;
}
}
void ADC_save()
{
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;
}
}
ISR(TIMER2_COMP_vect)
{
DAC_put();
if (j==10)//取樣頻率100hz
{
msk++;
ADC_save();
j=0;
}
j++;
if (msk == 200)
{
msk = 199;
}
}
```
:::
MATLAB
:::spoiler
```matlab=
clear;
close all;
clc;
portnum=5;
t=(0:0.01:1)';
p=sin(2*pi*5*t)+1.5;
%p(1:101)=3;
x=p(1:101);
x=single(x);
port=remo_open(portnum);%打開com5
[err]=remo_snput_matrix(port,x);
[input,err_2] = remo_snget_matrix(port);%得到ASA電腦給的矩陣
remo_close(port);%關閉com5
input=single(input);%轉型
%disp(input_2);%顯示outpute
plot(t(1:101),input(1:101),'r');
%legend(input_2)
```
:::
## 流程圖
**DAC介面卡原型試作-使用MCP4921 DAC輸出3V電壓,電表、示波器觀測**

**示波器觀察DAC輸出波型**

**ADC觀察DAC輸出波型**

## 實驗結果
**DAC介面卡原型試作-使用MCP4921 DAC輸出3V電壓,電表、示波器觀測**


**示波器觀察DAC輸出波型**
5hz弦波

10hz弦波

**ADC觀察DAC輸出波型**
5hz弦波

10hz弦波

## 遭遇問題
1. **示波器圖形呈階梯狀?**
因為當固定時間輸出類比電壓值時,輸出電壓會在一段時間內保持在恆定值,直到下一個輸出電壓輸出,所以會導致其在示波器看起來是一階一階的樣子,而ADC量測的話是因為DAC輸出一次ADC讀取一次,所以不會跑出階梯狀,而是一個一個不同的點相連而成的弦波。
*matlab 用plot的話不會有階梯狀,因為它會把兩個點連起來,實際上的數值要用stairs才能看吃來喔!!*
## 問題
1. **請問您在這個實驗中您學到了什麼?**
知道如何使用MCP4921 DAC輸出電壓,並且輸出弦波接著透過ADC去讀取。
2. **請問 D/A 輸出是否能夠有斜率的上昇或則下降, 為什麼?**
當頻率為5HZ時可以看到弦波有斜率的上升或下降,而在10HZ時看到的波偏向階梯狀,看不太出來其斜率上升下降,因為波的頻率變高,導致取樣點變少,看起來就會有階梯狀。取樣頻率為100hz
*你的取樣率有問題,太低了,可能是tim中斷頻率有問題*
## 驗收
用DAC來做16bitADC,並print出來ADC的結果。

### 驗收結果
$$
{\bf LSB}={工作電壓範圍 \over 幾位元}={3.84V \over 2^{16}}=0.00005859375V
$$
$$
{ADC*{V_{ref} \over 2^{16}}}={V_{in}}
$$
1. V input=2.88v
輸出16bitsADC為1011 1111 1011 1100=49084
$$
{49084*{3.84 \over 2^{16}}}={2.876V}
$$
電表量測2.87~2.88

結果

計算機

2. V input=1.92V
輸出16bitsADC為0111 1111 1110 1111=32751
$$
{32751*{3.84 \over 2^{16}}}={1.919V}
$$
電表量測1.91~1.92

結果

計算機
