|
第14章 電壓表\電流表\顯示器\計數器 通過本模塊的學習制作,可以進一步熟悉高精度ADC的運用,單片機 內部DataFlash的讀寫、動態顯示程序的編寫(包括小數的顯示)以及自 定義的SPI主從機數據通信原理,測試中,本模塊可以直接插接到第一章 介紹的單片機實驗板“通用計數器/顯示器接口”上使用。
0.png (237.53 KB, 下載次數: 197)
下載附件
2016-3-28 21:58 上傳
同一個模塊,通過硬件上的簡單設置可實現4種不同的功能
0.png (113.36 KB, 下載次數: 186)
下載附件
2016-3-28 21:59 上傳
K3(P4.2) | K4(P4.3) | 模塊功能 | | K1(P3.6) | K2(P3.7) | 電壓表檔位 | 輸入阻抗 | 0 | 0 | 電流表 | | 0 | 0 | 2.0480V | 2MΩ | 0 | 1 | 計數器 | | 0 | 1 | 20.480V | | 1 | 0 | 顯示器 | | 1 | 0 | 204.80V | | 1 | 1 | 電壓表(默認) | | 1 | 1 | 20.480V(默 認) | |
① 電壓表說明: 使用18位MCP3421A0T-E/CH芯片作ADC,通過軟件校準的方式可使整個模塊電壓測量誤差 最大值不大于量程的±0.05%,在電路上,2V檔由外部信號直接輸入ADC,其它檔位使用 1MΩ電阻降壓,然后使用MCP6V01T-E/SN 軌到軌自動調零運放作ADC輸入電壓跟隨器,由 于精度要求高,普通運放無法滿足要求。 ② 電流表說明: 使用0.1Ω/0.5W的電流取樣電阻,可測量0~2A范圍內的電流,取樣出來的電壓信號不經過電 壓跟隨器,直接送入ADC芯片。 ③ 通用顯示器說明: 外部單片機通過CLK與DAT兩條信號線向模塊送入數據,模塊能顯示0~99999范圍內整數或小 數值,外部單片機需要一次向模塊發送5個字節的數據,第1字節表示模塊地址,默認值為0 ,在多個模塊CLK與DAT并接在一起的情況下,只有與發送地址相符的模塊才接收與處理總線 上的數據,第2、3、4字節是需要顯示的數據,第2字節是數據高字節,第3字節是數據中間 字節,4字節是數據低字節,第5字節確定需要顯示的小數位數,所有字節都是按高位在前, 低位在后的順序發送,每一位的發送是外部單片機先把數據位放到DAT線上,然后拉低CLK線 ,模塊內部是在CLK下降沿后讀取DAT線上的狀態,經測試,模塊在22.1184MHz工作頻率下外 部單片機數據發送時鐘脈沖高電平1uS、低電平5uS條件下工作正常,也就是說發送一組數據 的最短時間需要大約(1 + 5)×8×5 = 240uS,為了提高穩定性,可以適當降低通信時鐘頻 率(主要是延長脈沖低電平時間),但也不能過低,要求一組數據必須在0.2秒內傳送完成 ,超過0.2秒,模塊自動清除前面已收到的不完整的數據,這樣保證了數據傳送的可靠性。 ④ 計數器說明: 外部脈沖信號通過DAT線送入模塊,每產生一個脈沖下降沿,計數器數值加1,超過最大值 99999后從0開始循環,要求外部脈沖信號低電平不能大于0.6V,高電平不能小于2V,高電平 最大值也不能超過40V,模塊具有斷電自動存儲數據的功能,可通過開關將CLK線接GND,上 電瞬間即可將顯示的計數值清零。
0.png (426.22 KB, 下載次數: 204)
下載附件
2016-3-28 22:02 上傳
- /*顯示程序,用STC15F2K60S2做顯示芯片
- // 硬件說明:P42 P43 P36 P37
- 0 0 電流表 0 0 1.9999V
- 0 1 計數器 0 1 19.999V
- 1 0 顯示器 1 0 199.99V
- 1 1 電壓表 1 1 999.99V
-
- // 待顯示數據由主機傳來,定義5個字節的顯示代碼
- // 第一字節是本機的地址碼,對本機而言,必須是“0”
- // 接下來三字節是要顯示的數據(高字節在前,低字節在后)
- // 第五個字節是要顯示的小數點位置,可能值是0-4,表示顯示幾位小數
- //分別代表:0——不顯示 1——十位,2——百位,3——千位 ,4——萬位 ,個位后小數點固定不顯示
- 99999 9999.9 999.99 99.999 9.9999
- //接線:P07 P06 P05 P04 P03 P02 P01 P00
- // e d h c g a f b
- //0 0 0 1 0 1 0 0 0 0x28
- //1 1 1 1 0 1 1 1 0 0xee
- //2 0 0 1 1 0 0 1 0 0x32
- //3 1 0 1 0 0 0 1 0 0xa2
- //4 1 1 1 0 0 1 0 0 0xe4
- //5 1 0 1 0 0 0 0 1 0xa1
- //6 0 0 1 0 0 0 0 1 0x21
- //7 1 1 1 0 1 0 1 0 0xea
- //8 0 0 1 0 0 0 0 0 0x20
- //9 1 0 1 0 0 0 0 0 0xa0
- //A 0 1 1 0 0 0 0 0 0x60
- //b 0 0 1 0 0 1 0 1 0x25
- //C 0 0 1 1 1 0 0 1 0x39
- //d 0 0 1 0 0 1 1 0 0x26
- //E 0 0 1 1 0 0 0 1 0x31
- //F 0 1 1 1 0 0 0 1 0x71
- //H 1 1 1 1 1 1 1 1 0xff
- //顯示位接線 P15 P14 P13 P16 P12
- // (高位)1 2 3 4 5(低位)
- //P32(INT0)接時鐘線,P33(INT1)接數據線*/
- #include "STC15F2K60S2.H " // 包含STC15F2K60S2單片機寄存器定義文件
- #include "UART.H"
- #include <intrins.h>
- #include "mcp3421.H"
- #include "myfun.h"
- #include "PowerDown_save.H"
- #define Hidden 16
- #define Address 0 // 本機地址,不同顯示模塊修改此數值即可
- #define TimeOver 500 // 定義一個超時值(5字節通信),500*2ms=1S,允許最慢2秒傳送完一幀數據
- sbit CLK=P3^2; // 外部輸入的時鐘端
- sbit DAT=P3^3; // 外部輸入的數據端
- unsigned char code DispCode[]={0x28,0xee,0x32,0xa2,0xe4,0xa1,0x21,0xea,0x20,0xa0,0x60,0x25,0x39,0x26,0x31,0x71,0xff};/*/顯示代碼*/
- unsigned char code DispBit[]={0xdf,0xef,0xf7,0xbf,0xfb}; //位碼表 (左邊最高位)11011111(P1) 11101111(P1) 11110111(P1) 10111111(P1) 11111011(P1)(右邊最低位)
- unsigned long DispData=0; // 用來顯示的值
- unsigned char DispBuf[5]={0,0,0,0,0}; // 顯示緩沖區 DispBuf[0]是左邊最高位 DispBuf[4]是右邊最低位
- unsigned char temp0,temp1,temp2,temp3,temp4; // 最高位 temp0 ,最低位 temp4,5位最大顯示99 999,只接收3字節,顯示緩沖5字節。
- unsigned char RecDatCount; // 接收的數據位數計數器
- unsigned char bdata RecDat; // 接收到的數據
- sbit RecDat0=RecDat^0; // 定義接收數據的末位為RecDat0,這要求RecDat位于bdata空間中
- bit ReciveOK ; // 接收完成標志,為1,說明已接收到完整的5個字節,可進行數據處理了
- unsigned char DotCnt=1; // 顯示小數位數。
- unsigned char InAddress=0; // 外部輸入地址,確定是否與本機地址相同
- bit StartOverCount; // 一旦有接收中斷,則置位該位,如果收到40位,則清該位
- unsigned int OverCount; // 超時計數器,當有接收中斷發生,就讓這個計數器開始計數(每個T0中
- // 斷計數一次,如果這個計數器計到了500(即1秒)仍沒有被清除,說明接收有誤,由主程序清 RecDatCount
- extern struct POWER_UP Power_up; // 計數器變量
- // 中斷程序用于完成5字節數據接收(1字節地址、3字節長整數、1字節小數位數說明)
- void ReciveDate() interrupt 0 // 外部中斷0(int0)中斷處理程序
- {
- if(StartOverCount==0) StartOverCount=1; // 開啟溢出計時器
- RecDatCount++; // 中斷次數(0-40)
- RecDat=RecDat<<1; // 主機是先發送高位,后發送低位
- if(DAT)
- RecDat0=1;
- else
- RecDat0=0;
- if(RecDatCount==8) // 接收完第一個字節
- {
- InAddress=RecDat; // 保存外部輸入的地址
- }
- else if(RecDatCount==16) // 第2個數據,長整數最高字節
- {
- if(InAddress==Address) // 地址相符才處理數據,方便多模塊并聯使用
- DispData=RecDat*65536;
- }
- else if(RecDatCount==24) // 第3個數據,長整數中間字節
- {
- if(InAddress==Address) // 地址相符才處理數據,方便多模塊并聯使用
- DispData=DispData+RecDat*256;
- }
- else if(RecDatCount==32) // 第4個數據,長整數最低字節
- {
- if(InAddress==Address) // 地址相符才處理數據,方便多模塊并聯使用
- DispData+=RecDat;
- }
- else if(RecDatCount==40) // 否則就是第5個數據,即小數點顯示位數
- {
- if(InAddress==Address) // 地址相符才處理數據,方便多模塊并聯使用
- DotCnt=RecDat;
- ReciveOK=1; // 要求刷新顯示器
- RecDatCount=0; // 中斷次數(0-40)
- StartOverCount=0; // 接收到40個字符,清標志
- OverCount=0; // 清超時計數器
- RecDat=0;
- }
- }
- void EX0_Init() // 外中斷0用于數據接收 CLK 輸入端口
- {
- IT0=1; // 外部引腳下降沿觸發(CLK時鐘信號)
- EX0=1; // 外中斷int0中斷允許
- PX0=1; // 將外中斷置為高級中斷,定時器T0低級中斷
- EA=1; // 比較器斷0電存儲只能使用低級中斷,無法更改。
- }
- void EX1_Init() // 外中斷1用于數據接收 DAT 輸入端口與計數器脈沖輸入口
- {
- IT1=1; // 設置為下降沿觸發,用于計數器脈沖輸入
- EX1=1; // 開外部中斷1
- PX1=1; // 將外中斷1置為高級中斷,定時器T0低級中斷
- EA=1; // 開總中斷
- }
- void Timer0_Init() // 初始化定時器0用于動態顯示程序
- {
- TMOD &= 0xF0; // 設置定時器模式
- TMOD |= 0x01; // 設置定時器模式
- TH0=(65536-4000)/256 ; // 計數脈沖周期 T=1/F= 1/(22.1184/12)= 0.5425uS
- TL0=(65536-4000)%256 ; // 4000*0.5425=2.17mS
- TR0=1; // T0開始運行
- ET0=1; // 定時器T0中斷允許
- PT0=0; // 將定時器置為低級中斷
- EA=1;
- }
- // 定時器 T0 用于完成5位數碼管論流點亮
- void Timer0() interrupt 1 // 定時器T0的中斷處理代碼
- {
- unsigned char temp; // 動態顯示中間變量
- static unsigned char Count; // 用于統計當前正顯示哪一位(先顯示左邊最高位)
- //********* 數據接收過程的時間限制 ************
- if(StartOverCount) // 如果要求計數的標志是1
- OverCount++; // 計數器加1
- //********* 正式動態顯示程序 ******************
- P1|=0x7c; // 關斷前次顯示0111 1100
- temp=DispBit[Count];
- P1&=temp; // 開啟P1位控制
-
- temp=DispBuf[Count]; // 5位顯示緩沖器BCD碼
- P0=DispCode[temp]; // 查字形碼表格(0-16)
-
- if(Count<4) // 顯示小數點(最右端小數點不顯示)
- {
- if(DotCnt==(4-Count))
- {
- P0&=0xDF; // 點亮小數點h位置 1101 1111
- }
- }
- Count++;
- if(Count==5) Count=0;
- TH0=(65536-4000)/256 ; // 計數脈沖周期 T=1/F= 1/(22.1184/12)= 0.5425uS
- TL0=(65536-4000)%256 ; // 4000*0.5425=2.17mS
- }
- void long_to_bcd(unsigned long temp)
- {
- unsigned char temp0,temp1,temp2,temp3,temp4; // 最高位 temp0 ,最低位 temp4,5位最大顯示99 999,只接收3字節,顯示緩沖5字節。
-
- temp%=100000; // 如果收到的數超過10 0000則僅取小于10 0000的值
- temp4 = temp % 10; // 獲得個位
- temp3 = temp / 10 % 10; // 獲得十位
- temp2 = temp / 100 % 10; // 獲得百位
- temp1 = temp / 1000 % 10; // 獲得千位
- temp0 = temp / 10000 % 10; // 獲得萬位
- if((temp0==0)&&(DotCnt<4)) // 如果最高位等于0,而顯示的小數位數小于4位
- DispBuf[0]=Hidden; // 那么最高位應該消隱
- else
- DispBuf[0]=temp0; // 否則將這個數送入最高位
- if((temp0==0)&&(temp1==0)&&(DotCnt<3)) // 最高位、次高位同時為0,且小數位數小于3位
- DispBuf[1]=Hidden;
- else
- DispBuf[1]=temp1;
- if((temp0==0)&&(temp1==0)&&(temp2==0)&&(DotCnt<2))
- // 最高位、次高位、第三位均為0,且小數位數小于2位時消隱
- DispBuf[2]=Hidden;
- else
- DispBuf[2]=temp2;
-
- if((temp0==0)&&(temp1==0)&&(temp2==0)&&(temp3==0)&&(DotCnt<1))
- // 最高位、次高位、第三位、第四位均為0,且小數位數小于1位(無)時消隱
- DispBuf[3]=Hidden;
- else
- DispBuf[3]=temp3;
- DispBuf[4]=temp4; // 最低位直接顯示
- }
- void main(void)
- {
- //////////////////////// 電壓電流表變量 ///////////////////
- unsigned char test_data[3]={0x00,0x00,0x00}; // 存放 MCP3421 AD轉換結果
- long aa; // 計算 MCP3421電壓值的中間變量
- float VIN3421; // 計算出來的MCP3421原始電壓
- long V3421; // 顯示時用于將數據擴大1000或10000倍
- ///////////////////////////////////////////////////////////
- UART_init(); // 串口初始化(占用定時器1)9600/22.1184MHz
- printf("串口初始化完畢");
- Timer0_Init(); // 初始化定時器0用于動態顯示程序
-
- ReciveOK=1; // 要求刷新顯示器
- CLK=1; // 數據接收端口初始化
- DAT=1;
- DispData=0X00; // 上電顯示0.0000
- DotCnt=4; // 上電顯示0.0000
- /*************************************************************************
- // 2A電流表,電流取樣電阻:0.1Ω/0.5W,
- *************************************************************************/
- if ((P42==0)&&(P43==0)) // 電流表
- {
- DotCnt=4; // 2V檔,保留4位小數,2.0480A
- WrToMCP3421(SlaveADDR, 0x9C); // 1001 1100 18位分辨率
- delay300ms();
- while(1)
- {
- aa=test_data[0]<<8;
- aa=aa+test_data[1];
- aa=aa<<8;
- aa=aa+ test_data[2];
- VIN3421=2.048*aa/131071; // 得到取樣電阻上的電壓值
-
- VIN3421=VIN3421/0.1; // I=U/R
- VIN3421=VIN3421-0.000; // 要求零輸入零輸出
- if (VIN3421<0 ) VIN3421=0; // 數碼管沒編寫顯示負數的功能,電腦能直接顯示負數
-
- printf("I= :%.5f ",VIN3421);
- // 浮點數轉長整數
- V3421=VIN3421*10000; // 2V檔,保留4位小數 ,2.0480
- long_to_bcd(V3421);
-
- delay300ms(); // 延時避免硬接頻繁操作
- delay300ms();
- }
- }
- /*************************************************************************
- // 計數器, DAT為計數脈沖輸入端,上電瞬間如果CLK=0則清除計數值
- *************************************************************************/
- if ((P42==0)&&(P43==1))
- {
- DotCnt=0; // 計數器不顯示小數
- ReadFLASH(); // 讀取單片機內部FLASH中保存的重要數據,只需2個時鐘。
- comparator_init(); // 比較器掉電中斷初始化
- EraseFLASH(); // 扇區擦除需要21mS
- EX1_Init(); // 外部計數端口初始化
- while(1);
- }
- /*************************************************************************
- // 5位通用顯示器
- *************************************************************************/
- if ((P42==1)&&(P43==0))
- {
- DotCnt =4; // 上電時顯示0.0000
- EX0_Init(); // 外中斷0用于數據接收時鐘輸入端口
- while(1)
- {
- if(ReciveOK) // 如果收到了40位數據,將數值轉BCD碼放入顯示緩沖器。
- {
- long_to_bcd(DispData);
- ReciveOK=0;
- }
- if(OverCount>=TimeOver) // 出現了超時錯誤
- {
- RecDatCount=0; // 將接收計數器清零
- StartOverCount=0; // 接收到40個字符,清除計數標志
- OverCount=0; // 清超時計數器
- }
- }
- }
- /*************************************************************************
- // 18位分辨率電壓表
- *************************************************************************/
- if ((P42==1)&&(P43==1))
- {
- if ((P36==0)&&(P37==0)) DotCnt=4; // 2V檔,保留4位小數 ,2.0480
- if ((P36==0)&&(P37==1)) DotCnt=3; // 20V檔,保留3位小數, 20.048
- if ((P36==1)&&(P37==0)) DotCnt=2; // 200V檔,保留2位小數, 200.48
- if ((P36==1)&&(P37==1)) DotCnt=2; // 1000V檔,保留2位小數, 999.99
-
- WrToMCP3421(SlaveADDR, 0x9C); // 1001 1100 18位分辨率
- delay300ms();
-
- while(1)
- {
- RdFromMCP3421(SlaveADDR, test_data,3); //連續讀取3個字節數據
- aa=test_data[0]<<8;
- aa=aa+test_data[1];
- aa=aa<<8;
- aa=aa+ test_data[2];
- VIN3421=2.048*aa/131071;
- if ((P36==0)&&(P37==0)) // 2V檔
- {
- VIN3421=VIN3421*1.00; // 2V檔,無衰減,精密校準
- printf("2V :%.5f ",VIN3421);
- V3421=VIN3421*10000; // 2V檔,保留4位小數 ,2.0480
- }
- if ((P36==0)&&(P37==1)) // 20V檔
- {
- VIN3421=VIN3421*10.00; // 20V檔,10倍衰減+衰減電阻誤差補償
- printf("20V :%.4f ",VIN3421);
- V3421=VIN3421*1000; // 20V檔,保留3位小數 ,20.480
- }
- if ((P36==1)&&(P37==0)) // 200V檔
- {
- VIN3421=VIN3421*100.00; // 200V檔,100倍衰減+衰減電阻誤差補償
- printf("200V :%.3f ",VIN3421);
- V3421=VIN3421*100; // 200V檔,保留2位小數,204.80
- }
- if ((P36==1)&&(P37==1)) // 1000V檔
- {
- VIN3421=VIN3421*1.00; // 2V檔,無衰減,精密校準
- printf("2V :%.5f ",VIN3421);
- V3421=VIN3421*10000; // 2V檔,保留4位小數 ,2.0480
- }
-
- VIN3421=VIN3421*10.00; // 20V檔,10倍衰減+衰減電阻誤差補償
- //
- VIN3421=VIN3421-0.000; // 要求零輸入零輸出
- if (VIN3421<0 ) VIN3421=0; // 數碼管沒編寫顯示負數的功能,電腦能直接顯示負數
-
- printf("20V :%.5f ",VIN3421);
- // 浮點數轉長整數
- if ((P36==0)&&(P37==0)) V3421=VIN3421*10000; // 2V檔,保留4位小數 ,2.0480
- if ((P36==0)&&(P37==1)) V3421=VIN3421*1000; // 20V檔,保留3位小數, 20.048
- if ((P36==1)&&(P37==0)) V3421=VIN3421*100; // 200V檔,保留2位小數, 200.48
- if ((P36==1)&&(P37==1)) V3421=VIN3421*100; // 1000V檔,保留2位小數, 999.99
- long_to_bcd(V3421);
-
- delay300ms(); // 延時避免硬接頻繁操作
- delay300ms();
- }
- }
- }
- void X1_ISR(void) interrupt 2 // 外部中斷1中斷函數實現計數功能
- {
- Power_up.times++;
- long_to_bcd(Power_up.times);
- }
復制代碼
|
|