51匯流排方式獲取adc0809數值
最近微控制器有一個實驗挺有意思的,使用51微控制器以匯流排的方式讀取adc0809的資料
先補充點關於微控制器匯流排的預備知識,我一開始不瞭解匯流排的時候做這個實驗也是很懵逼的。
微控制器的三匯流排結構

51微控制器有三條匯流排:資料匯流排、地址匯流排、控制匯流排
從圖中可以看出,8位資料匯流排由P0組成,16位地址匯流排由P0和P2組成,控制匯流排由P3和相關引腳組成
採用匯流排的方式可以簡化程式設計,節省I/O口,便於外設擴充套件
但是資料口和地址口在P0是怎麼複用的呢,這就需要看到時序了

從圖中可以看出,P0口是資料/地址分時複用的,這是P0口內部的複用結構完成的
實操練習
51微控制器與adc0809接線原理圖如下

解釋電路
P2.7口用作adc0809的選擇線
P0.0~P0.2所接的A B C是adc0809的IN0通道選擇線
接下來就是計算adc0809的地址了
P2 P0
0xxx xxxx xxxx x000
因此地址為0x7ff8
遇到的問題
本來應該顯示5v的位置只顯示1.144v,而且在電阻增大的過程中,顯示的值先減小後增大又減小,具體情況如圖


實在沒有辦法的情況下,借別的同學的程式碼來看,沒發現自己的程式在時序、地址上的錯誤。
琢磨了微控制器的數值變換的現象後,突然覺得是不是儲存ad轉換數值的變數溢位了,然後就發現我的變數型別是int,而別人的變數型別是long int
在將儲存ad轉換的變數型別修改過後,程式就執行正常了

程式程式碼
#include <reg51.h> #include <absacc.h> typedef unsigned char uchar; typedef unsigned int uint8; typedef unsigned long int uint16; uchar led_mod[] = {0x3f,0x06,0x5b,0x4f,//!< 數碼管編碼 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}; #define AD_IN0 XBYTE [0x7ff8] sbit EOC = P3^5; sbit CLK = P3^3; sbit seg1 = P2^0; sbit seg2 = P2^1; sbit seg3 = P2^2; sbit seg4 = P2^3; uint16 adc_data = 0;//> 儲存ad轉換結果 /** * @brief 延遲函式 * */ void delay_ms(uint8 time) { uint8 j; for (; time>0; time--) { for(j=114; j>0; j--); } } /** * @brief 定時器初始化 * */ void timer_init(void) { TMOD |= 0x02; TH0 = 200;//> 產生方波週期2us TL0 = 200; ET0 = 1; TR0 = 1; } /** * @brief 數碼管動態顯示函式 * */ void display(void) { adc_data = adc_data*1000/51;//> 解析度為5/256約為1/51 P1 = 0x00; P1 = led_mod[adc_data/1000]|0x80; seg1 = 0; delay_ms(2); seg1 = 1; P1 = 0x00; P1 = led_mod[(adc_data%1000)/100]; seg2 = 0; delay_ms(2); seg2 = 1; P1 = 0x00; P1 = led_mod[(adc_data%100)/10]; seg3 = 0; delay_ms(2); seg3 = 1; P1 = 0x00; P1 = led_mod[adc_data%10]; seg4 = 0; delay_ms(2); seg4 = 1; } void main() { timer_init(); EA = 1; while(1) { AD_IN0 = 0; while(EOC == 0); adc_data = AD_IN0; display(); } } /** * @brief 產生時鐘週期 * */ void timer0() interrupt 1 { CLK = ~CLK; }
