#include <STC12C5A60S2.H>
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
/*Define ADC operation const for ADC_CONTR*/
#define ADC_POWER 0x80 //ADC 電源控制位 10000000 0X80 /
#define ADC_FLAG 0x10 //ADC 完成標志 00010000 0x10
#define ADC_START 0x08 //ADC 啟動ADC開關 00001000 0x08 /
#define ADC_SPEEDLL 0x00 //420 轉換速度 00000000 0x00 /
#define ADC_SPEEDL 0x20 //280 轉換速度 00100000 0x20
#define ADC_SPEEDH 0x40 //140 轉換速度 01000000 0x40
#define ADC_SPEEDHH 0x60 //70 轉換速度 01100000 0x60
/*----------------------------------------------------------------------------------------------
meidangzuidivoltage:表示AD最高電壓5V對應10bit_AD轉換的最小電壓是多少 5/1024= 0.00488V 也就是AD在
//10bit下測量的最小電壓,
//那么在計算AD電壓值的時候將公式 “GetADCResult(ch)*5/1024”分兩步進行 方便后面對數據分離顯示到
//1602上面 先計算ADC_jiancedaozuidivoltage=5*10000000/1024 先將這個數放大100萬倍
//最后在算AD_jie_guo=(ADC_jiancedaozuidivoltage*GetADCResult(ch);
----------------------------------------------------------------------------------------------*/
ulong ADC_jiancedaozuidivoltage,temp,AD_jie_guo; //長整形數據 16bit
float OVP; //定義一個浮點數 以保留小數點 提高進度
uint num,i,vlue;//ADC_mV,ADC_RESX,VCC_V=5.2;
sbit lcden = P2^7;
sbit rs = P2^6;
sbit rw = P2^5;
sbit LED = P3^0;
void Delay(uint n); //延時函數
uint GetADCResult(uchar ch); // ad轉換函數
void InitADC(); //ad初始化函數
void OCP_1(); //過壓 欠壓 提醒函數聲明
float count(uchar ch); //AD值100次平均值函數
//ulong count(uchar ch); //ad計算函數測量0-5V時候用的
uchar code table[20]= {" Shu Kong QuDong"}; // 開機畫面的布置
uchar code table2[20]={" CQDZ Alan V1.01"};
/*-----------------
延時函數
-----------------*/
void delayms(uint xms) //延時函數
{
uint i,j;
for(i=xms;i>0;i--)
for (j=960;j>0;j--);
}
/*--------------------
寫命令
---------------------*/
void lcd_write_com(uchar com)
{
rw=0;
rs=0; //寫命令狀態
P0=com;
lcden=1;
delayms(5);
lcden=0;
}
/**-----------------
寫數據
--------------- ***/
void write_date_(uchar date)
{
rw=0; //寫數據
rs=1; //寫數據狀態
P0=date;
delayms(5);
lcden=1; //使能
delayms(5);
lcden=0;
}
/*---------------
初始化顯示屏
---------------*/
void lcd_init(void)
{
lcden=0;
lcd_write_com(0x38); //設置8位格式,2行,5*7
lcd_write_com(0x0c); //整體顯示,關光標,不顯示
lcd_write_com(0x06); // 設定輸入方式,增量不移位
lcd_write_com(0x01);//清屏幕
delayms(5); //延?
}
/*-----------------------
函數名稱 格式定義
函數的介紹 在某個屏幕位置上顯示一個字符,X(0-16),y(1-2)
X:表示字的格式 一共16個 Y:表示行 一共2行
-------------------------*/
//格式定義
void lcd_disp_char(uchar y,uchar x, uint dat)
{
uint address;
if(y==1) //y為1 在第一行
address=0x80+0x10+4+x; //整屏左移動以后 從新定義新的起始位置 但是要加上之前移動后的地址
else
address=0xc0+0x10+x; //y為2 在第二行 X顯示字的位置 0XC0是 0x80+0x40的結果
lcd_write_com(address); //寫入要寫的位置
write_date_( dat); //寫入你要寫的數據
}
/*------------------
顯示函數2
-------------------*/
void disp()
{
AD_jie_guo = count(0); //經過上面的計算求出來100次的平均值存放在AD_jie_guo里面
/*-------------------------------------------------------------------------------
//擴大電壓 我的量程是0-30V 分壓電阻是 10k 2k 電阻比的6 反推 當測試電壓為5v
//的時候 最高電壓為30V 測量后調試OK 因電阻誤差 調整了數據為6.02
//同時這里也可以用(temp/0.167)/100 這個是電壓比也就是30V分壓為5V
//然后5/30=0.167的結果也是一樣的
---------------------------------------------------------------------------------*/
temp=((ADC_jiancedaozuidivoltage*AD_jie_guo)*6.02)/100;
// temp=(temp*6.02)/100; //備用算法 這樣太占用位置 我把這步合并到上面了
//0x30是顯示數字 0-9 30表示第一個數0
lcd_disp_char(1,0, temp%10000000/1000000+0x30 ); //十位
lcd_disp_char(1,1, temp%1000000/100000+0x30); //個位
lcd_disp_char(1,2,'.' ); // 小數點
lcd_disp_char(1,3, temp%100000/10000+0x30 ); //個分位
lcd_disp_char(1,4, temp%10000/10000+0x30 ); //百分位
lcd_disp_char(1,5,'V' );
}
//count(0)
/*-----------------------------------
名稱 開機畫面
功能 開機的時候顯示一下銘牌
for來完成 屏幕左移動
----------------------------------*/
void init()
{
// lcd_write_com(0x80+0x10); //定義顯示的位置 起始地址
lcd_write_com(0x80); //定義顯示的位置 起始地址
for(num=0;num<20;num++)
{
write_date_(table[num]); //初始化屏幕的初始數字“0000”
delayms(5);
}
// lcd_write_com(0x80+0x40+0x10); //定義第二排,顯示的地址 0x80是顯示屏寄存器第一排起始地址
lcd_write_com(0xc0); //定義顯示的位置 起始地址
for(num=0;num<20;num++) //0x40是第二排起始地址
{
write_date_(table2[num]);
delayms(5);
}
for(num=0;num<20;num++) //整屏左移動 這里的21就是指可以移動多少格
{ //可以是100可以是1000 相當于是電子屏幕一樣
lcd_write_com(0x18); //0x18是整屏左移 指令
delayms(50);
}
}
void main()
{
lcd_init();
init();
InitADC(); //Init ADC sfr
ADC_jiancedaozuidivoltage=(49600000)/1024; //5V參考電壓,計算分度值的時候,數值過小會出現很大偏差
//所以放大1000萬倍 這樣精度會高一些
while (1)
{
disp();
OCP_1();
}
}
/*************************
* 功能 ADC轉換函數
* 帶結果返回 帶結果返回
**************************/
uint GetADCResult(uchar ch) //參數的定義“ch”初始化就是為0 所以這里的“ch”應該是用來設置用哪個端口
{ //來設置AD的 這個“ADC_POWER|ADC_SPEEDLL|ch|ADC_START”;或出來的結果是
//10001000 后面三個0對應的是 CHS2 CHS1 CHS0 查表格 剛好是P1.0口
//作為AD模擬輸入的。如果要改變端口就要給ch賦值 假如要P1.2 就需要賦值為ch=0x02;
ADC_CONTR = ADC_POWER|ADC_SPEEDLL|ch|ADC_START; //配置相關項目打開
_nop_();
_nop_();
_nop_(); //4個機器時間的延時 以保障轉換完成
_nop_();
while(!(ADC_CONTR&ADC_FLAG)); //判斷是否轉換完成
ADC_CONTR &= ~ADC_FLAG; //關閉ADC同時清零
vlue = (ADC_RES*4+ADC_RESL); //返回結果10bit 也就是電壓值 ,左移幾次就乘以2的幾次方
//右移幾次就除以2的幾次方 這里的ADC_RES*4相當于ADC_RES<<2 左移了2
Delay(2); //延時一下
return vlue;
}
/******************************
* 功能:計算1結果函數 將10bit的結果計算為對應電壓值MV
* 函數類型 float 型 浮點型
* 帶結果返回 帶結果返回
******************************/
/*
ulong count(uchar ch)
{
ADC_RESX=GetADCResult(ch);
ADC_mV=(VCC_V*(long)ADC_RESX*10000/1024+5)/10;//強制轉換長整型運算,得到結果mV(4舍5入)
return ADC_mV;
}*/
/******************************
* 功能:計算2結果函數 將10bit的結果計算為對應電壓值MV
* 函數類型 float 型 浮點型 保留小數點 提高精度
* 帶結果返回 帶結果返回
* 同時對GetADCResult(ch)采樣100次求平均值 提高進度
******************************/
float count(uchar ch)
{
float AD_val; //定義處理后的數值AD_val為浮點數 初始狀態AD_val為0 經過100次變化后
uchar n; //AD_val值為100次的GetADCResult(ch)這個變化值 然后在除以100就是平均值
for(n=0;n<100;n++)
AD_val += GetADCResult(ch); //轉換100次求平均值(提高精度)
AD_val /= 100; //意思是GetADCResult(ch)經過了100次轉換后的值保存到了AD_val里面 然后在
//除以100 就是求平均值 這樣的精度高一點
// AD_val=(AD_val*5)/1024; //AD的參考電壓是單片機上的5v,所以乘5即為實際電壓值,因為為分段計算這個就
//暫時不用 如果測量0-5v就可以這樣計算
OVP = ( AD_val*4.96 ) / 1024; //為過壓 欠壓 提醒做 前期的計算 方便后面使用
return AD_val;
}
/*-------------------------------------------------------------------------------
函數功能 過壓ovp 欠壓 檢測 cpu供電VCC測量的是4.96V 本程序用的也是4.96V
下面計算也用4.96V。
計算方案:
通過計算AD基礎值變化也就是"0-5V"的變化情況,然后人為的乘以擴量程倍率“6.02”
得到的就是要過壓去,欠壓保護的值。
例如:現在要電壓達到10v的時候啟動欠壓提醒
計算過程:方法一
本次計算實際是通過vlue = (ADC_RES*4+ADC_RESL);在0-1024之間變化的值來確認模擬
電壓輸入是多少的 如 1024/4.96V(VCC)*(VIN)模擬量,那么對應10v的模擬量電壓應該為
10/6.02=1.7V,那么它vlue = (ADC_RES*4+ADC_RESL)就要變化到多少才是對應的1.7V呢,
通過公式1024/4.96V(vcc)*1.7V(vin)=350;
通過計算它的值在1.7V的時候vlue = (ADC_RES*4+ADC_RESL)=350. 那么我們通過判斷
這個vlue = (ADC_RES*4+ADC_RESL)的數據也可以判斷對應保護電壓,但是計算很麻煩
所以我們把這個麻煩的過程直接讓程序計算好,然后直接用對應系數1.7V即可。
方法二
這個就是我在float count(uchar ch)函數里面計算好了的(OVP)如下
OVP =( AD_val*4.96 ) / 1024;
OVP必須是float類型的數據保留小數點 不然誤差大,只要保留了小數點誤差非常小
通過上面計算好的這個過程然后我們要計算欠壓就非常好算了,例如我要12V欠壓
直接用12V/6.02這個系數 得到的就是你要的AD模擬量大小 12/6.02約等于2.0V
只要判斷OVP到了2.0就說明12V到了 做什么處理就看使用者了 可以報警 可以切斷電源
本程序我是10V欠壓 計算 10/6.02約等于1.7V 我判斷是OVP>1.7 也就是對應的欠壓過壓點
到了 我是點亮LED,低于1.7也就是10V 就關閉LED;
----------------------------------------------------------------------------------*/
void OCP_1()
{
if(OVP > 1.7) //高于10V 10/6.02=1.7
{
LED = 0;
// 調試的時候用的 通過判斷temp來判斷是否過壓 如果過壓就啟動保護
}
if(OVP < 1.7) //低于10v 10/6.02=1.7
{
LED = 1;
}
}
/*----------------------------
功能 ADC初始化函數
----------------------------*/
void InitADC() //adc初始化函數
{
P1M1 = 0x01; //設置P1.0高阻模式
P1M0 = 0x00; //設置P1.0高阻模式
P1ASF = 0x01; //開放P1.0通道ADC功能 就是8個I/O口全部開放
ADC_RES = 0; //清除之前的結果
ADC_RESL = 0;
ADC_CONTR = ADC_POWER | ADC_SPEEDLL; //打開AD電壓 轉換速度540
Delay(2); //ADC 延時一下
}
/*----------------------------
延時函數
----------------------------*/
void Delay(uint n)
{
uint x;
while (n--)
{
x = 5000;
while (x--);
}
}
|