2018學生科協硬體方向培訓——SPI、ADC
2018學生科協硬體方向培訓——SPI、ADC
本週培訓主要內容都在這裡啦,大家記得自己嘗試操作操作哦:
目錄
SPI簡介
SPI匯流排概念
- SPI介面的全稱是“Serial Peripheral Interface”,意為序列外圍介面
- SPI介面主要應用在
EEPROM
,FLASH
,實時時鐘
,AD轉換器
,還有數字訊號處理器
和數字訊號解碼器
之間。 - SPI介面是在CPU和外圍低速器件之間進行同步序列資料傳輸,在主器件的移位脈衝下,資料按位傳輸,高位在前,低位在後,為
全雙工通訊
,資料傳輸速度總體來說比I2C匯流排要快
SPI介面是以主從方式工作的,這種模式通常有一個主器件和一個或多個從器件,其介面包括以下四種訊號:
- MOSI
主器件資料輸出,從器件資料輸入
- MISO
主器件資料輸入,從器件資料輸出
- SCLK
時鐘訊號,由主器件產生
- /CS
從器件使能訊號,由主器件控制,低電平有效
(圖中NSS)
模擬SPI程式設計規則
模擬SPI就是按照SPI標準的時序,採用單片機發出時鐘訊號,發出或接受資訊的過程
模擬SPI寫函式
根據以上時序圖我們可以寫出以下函式
void SPI_Write(uchar dat)//dat為需要寫入的指令
{
uchar i;
CLK = 0;
for(i=0; i<8; i++)//dat為8位所以迴圈8次
{
DIN = dat >> 7; //放置最高位 DIN為資料管腳可自行定義
dat <<= 1;//SPI自高向低位傳輸所以每一次向右移動一位
CLK = 0; //模擬時鐘訊號,上升沿放置資料
CLK = 1;
}
}
模擬SPI讀函式
uint SPI_Read(void) //返回dat值,此函式返回了12位,所以uchar會溢位
{
uint i, dat=0;
CLK = 0;
for(i=0; i<12; i++) //接收12位資料
{ //i表示接收位數
dat <<= 1;
CLK = 1; //模擬時鐘訊號
CLK = 0;
dat |= DOUT; //接收資料的管腳
}
return dat;
}
ADC簡介
AD/DA的概念
模擬訊號(Analog signal):
主要是與離散的數字訊號相對的連續訊號。模擬訊號是指資訊引數在給定範圍內表現為連續的訊號。 或在一段連續的時間間隔內,其代表資訊的特徵量可以在任意瞬間呈現為任意數值的訊號。
——(https://baike.baidu.com/item/%E6%A8%A1%E6%8B%9F%E4%BF%A1%E5%8F%B7/706796)數字訊號(Digital signal):
是離散時間訊號(discrete-time signal)的數字化表示,通常可由模擬訊號(analog signal)獲得。數字訊號指自變數是離散的、因變數也是離散的訊號,這種訊號的自變數用整數表示,因變數用有限數字中的一個數字來表示。
——(https://baike.baidu.com/item/%E6%95%B0%E5%AD%97%E4%BF%A1%E5%8F%B7/915663?fr=aladdin)AD: 模數轉換,將模擬訊號變成數字訊號,便於數字裝置處理。
DA: 數模轉換,將數字訊號轉換為模擬訊號與外部世界介面。
ADC主要引數
- ADC的解析度
- 量化誤差
- 偏移誤差
- 滿刻度誤差
- 線性度
- 絕對精度
- 轉換速率
- 在這裡我們詳細講一下ADC的解析度
ADC的解析度是指使輸出數字量變化一個相鄰數碼所需輸入模擬電壓的變化量。常用二進位制的位數表示。
例如12位ADC的解析度就是12位,或者說解析度為滿刻度的1/(2^12)。一個10V滿刻度的12位ADC能分辨輸入電壓變化最小值是10V×1/(2^12 )=2.4mV。
利用SPI協議讀取XPT2046的AD轉化值
下圖為TPX2046採集電位器電壓訊號的演示
XPT2046簡介
- XPT2046 是一款4 線制電阻式觸控式螢幕控制器,內含12 位解析度125KHz 轉換速率
逐步逼近
A/D 轉換器。(有關ADC的型別大家可以自行百度)下表為管腳屬性。
XPT2046程式設計
在操作晶片時,首先我們需要確定操作的時序,在使用者手冊中,我們可以找到這樣的時序圖:
我們可以看出,與XPT2046的通訊主要有以下幾個步驟
- 將
CS
訊號拉低 - 通過
DIN
傳送同步訊號的指令(8位) BUSY
被拉高(注意,BUSY高電平持續一個脈衝週期)DOUT
返回資料CS
訊號被拉高,資料傳輸結束
理解了時序圖之後,我們需要藉助控制命令表來確定我們所需要的命令
- 這次我們需要讀取的用的是單通道ADC值,並將晶片設定為節能模式,所以我們的控制字應為
1 _ _ _ 0100
(空缺位置,可以用A2 A1 A0控制AD轉化的通道)
以下為XPT2046程式設計程式碼
uint Read_AD_Data(uchar cmd)//寫入命令,函式中返回uint型
{
uchar i;
uint AD_Value;
CLK = 0;//初始化時鐘訊號為低電平
CS = 0;//拉低片選訊號
SPI_Write(cmd);//這裡的函式在SPI部分
_nop_(); //等待晶片穩定
_nop_();
CLK = 0; //拉低時鐘訊號,以便之後讀取AD值
_nop_();
_nop_();
AD_Value=SPI_Read();//讀取AD值這裡的函式在SPI部分
CS = 1;
return AD_Value;
}
培訓程式碼
- 需要連線P0到數碼管
- P2.2,P2.3,P2.4分別連線LSA,LSB,LSC
- P3.4,P3.5,P3.6,P3.7分別連線到DIN,CS,CLK,DOUT
- 可以根據原理圖調整 A2 A1 A0的值檢測不同通道的AD值
/**************************************************************************************
2018科協硬體培訓——電位器AD實驗
實現現象: 下載程式後數碼管前4位顯示電位器檢測的AD值,範圍是0-4095,一般達不到最大
***************************************************************************************/
#include"reg52.h"
#include"intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit DOUT = P3^7; //輸出
sbit CLK = P3^6; //時鐘
sbit DIN = P3^4; //輸入
sbit CS = P3^5; //片選
typedef unsigned int u16; //對資料型別進行宣告定義
typedef unsigned char u8;
u8 disp[4];
u8 code smgduan[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void SPI_Write(uchar dat);
uint SPI_Read(void);
uint Read_AD_Data(uchar cmd);
void DigDisplay();
void delay(u16 i);
void datapros();
void main()
{
while(1)
{
datapros();
DigDisplay();
}
}
void SPI_Write(uchar dat)
{
uchar i;
CLK = 0;
for(i=0; i<8; i++)
{
DIN = dat >> 7;
dat <<= 1;
CLK = 0;
CLK = 1;
}
}
uint SPI_Read(void)
{
uint i, dat=0;
CLK = 0;
for(i=0; i<12; i++)
{
dat <<= 1;
CLK = 1;
CLK = 0;
dat |= DOUT;
}
return dat;
}
uint Read_AD_Data(uchar cmd)
{
uchar i;
uint AD_Value;
CLK = 0;
CS = 0;
SPI_Write(cmd);
for(i=6; i>0; i--); //延時等待轉換結果
CLK = 1; //傳送一個時鐘週期,清除BUSY
_nop_();
_nop_();
CLK = 0;
_nop_();
_nop_();
AD_Value=SPI_Read();
CS = 1;
return AD_Value;
}
void DigDisplay()
{
u8 i;
for(i=0;i<4;i++)
{
switch(i) //位選,選擇點亮的數碼管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;//顯示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//顯示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//顯示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//顯示第3位
}
P0=disp[i];//傳送資料
delay(100); //間隔一段時間掃描
P0=0x00;//消隱
}
}
void delay(u16 i)
{
while(i--);
}
void datapros()
{
u16 temp;
static u8 i;
if(i==50)
{
i=0;
temp = Read_AD_Data(0x94); // AIN0 電位器
}
i++;
disp[0]=smgduan[temp/1000];//千位
disp[1]=smgduan[temp%1000/100];//百位
disp[2]=smgduan[temp%1000%100/10];//十位
disp[3]=smgduan[temp%1000%100%10];
}