想用STC90C51的P1.0輸出受PID運算結果控制,頻率為0~10KHZ可變的方波,但是被數碼管顯示占用了時間波形頻率最高才500.
有什么辦法可以解決嘛。
/*-----------------------------------------------
IIC協議 PCF8591 AD/DA轉換
P3 獨立按鍵輸入
P1 LED數碼管動態掃描輸入
------------------------------------------------*/
#include <reg52.h>
#include <stdio.h>
#include "i2c.h"
#include "delay.h"
#include "display.h"
#define uchar unsigned char
#define uint unsigned int
#define AddWr 0x90 //寫數據地址
#define AddRd 0x91 //讀數據地址
#define KeyPort P3 //定義按鍵輸入端口
#define Key_Add 8
#define Key_Dec 7
#define Key_Stop 4
#define Key_Change 3
#define Key_Add10 5
#define Key_Add100 6
#define Key_Dec10 2
#define Key_Dec100 1 //定義按鍵操作id
sbit PWMOUT =P1^0; //pwm輸出
sbit LED1 =P1^1;
sbit LED2 =P1^2;
sbit LED3 =P1^3;
bit StopFlag = 0; //暫停標志
extern bit ack; //IIC應答
int KeyValue = 0;
bit ShowType = 0;
//******************按鍵掃描**********************
//***********************************************
unsigned char KeyScan(void)//鍵盤掃描
{
unsigned char keyvalue;
if(KeyPort!=0xff)
{
DelayMs(10);
if(KeyPort!=0xff)
{
keyvalue=KeyPort; //獲取按鍵值
while(KeyPort!=0xff);
switch(keyvalue)
{
case 0xfe:return Key_Dec100;break; //k1
case 0xfd:return Key_Dec10;break; //k2
case 0xfb:return Key_Change;break; //k3
case 0xf7:return Key_Stop;break; //k4
case 0xef:return Key_Add100;break; //k5
case 0xdf:return Key_Add10;break; //k6
case 0xbf:return Key_Dec;break; //k7
case 0x7f:return Key_Add;break; // k8 返回按鍵操作id,交換返回值改變每個按鍵功能
default:return 0;break;
}
}
}
return 0;
}
//***********************************************
//***********************************************
//***********************************************
int abs(int a)
{
if(a<0)
a=-a;
return a;
}
//***********************************************
//***********************************************
//******************定義PID結構體**********************
typedef struct PID
{
int SetPoint; //設定目標Desired Value
double Proportion; //比例常數Proportional Const
double Integral; //積分常數Integral Const
double Derivative; //微分常數Derivative Const
int LastError; //Error[-1]
int PrevError; //Error[-2]
} PID;
//******************定義相關宏*************************
#define P_DATA 10 //比例系數p
#define I_DATA 2 //積分系數i
#define D_DATA 1 //微分系數d 都需要后期調整
//*****************聲明PID實體*************************
static PID sPID;
static PID *sptr = &sPID;
int pwmValue = 0;
int PWM;
int SetVol =500; //預設電壓值,可改變!!0-500
int value_V=0;
//*****************定時計數器定義****************************
uchar TimerCount = 0;
int PwmCount = 0; //pwm定時器計數
//*********************PID參數初始化*******************
void IncPIDInit(void)
{
sptr->LastError = 0; //Error[-1]
sptr->PrevError = 0; //Error[-2]
sptr->Proportion =P_DATA; //比例常數Proportional Const
sptr->Integral =I_DATA; //積分常數Integral Const
sptr->Derivative =D_DATA; //微分常數Derivative Const
sptr->SetPoint =SetVol; //目標為宏定義
}
//*******************增量式PID控制設計*****************
int IncPIDCalc(int NextPoint)
{
int iError, iIncpid; //當前誤差
sptr->SetPoint =SetVol; //設定目標值
iError = sptr->SetPoint - NextPoint; //增量計算
if(abs(sptr->LastError-iError)<20)return 0 ; //50該值需要實際調試
iIncpid = sptr->Proportion * iError //E[k]項
- sptr->Integral * sptr->LastError //E[k-1]項
+ sptr->Derivative * sptr->PrevError; //E[k-2]項
sptr->PrevError = sptr->LastError; //存儲誤差,用于下次計算
sptr->LastError = iError; //存儲誤差
return iIncpid ; //返回增量值
}
/*------------------------------------------------
定時器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定時器,使用"|"符號可以在使用多個定時器時不受影響
TH0=(65536-45872)/256; //定時時間高八位初值
TL0=(65536-45872)%256; //定時時間低八位初值
EA=1; //總中斷打開
ET0=1; //定時器中斷打開
TR0=1; //定時器開關打開
}
void Init_Timer1(void)
{
TMOD |= 0x10;
TH1 = (65536-100)/256;//
TL1 = (65536-100)%256;
ET1=1; //打開定時器中斷
EA=1; //打開總中斷
TR1=1; //打開定時器開關
}
/*------------------------------------------------
讀AD轉值程序
輸入參數 Chl 表示需要轉換的通道,范圍從0-3
返回值范圍0-255
------------------------------------------------*/
unsigned int ReadADC(unsigned char Chl)
{
unsigned int Val;
Start_I2c(); //啟動總線
SendByte(AddWr); //發送器件地址
if(ack==0)return(0);
SendByte(0x40|Chl); //發送器件子地址
if(ack==0)return(0);
Start_I2c();
SendByte(AddWr+1);
if(ack==0)return(0);
Val=RcvByte();
Val = Val*0.01953*100; //每讀取到一個1就表示5/256V(0.01953)最后保留兩位小數//
NoAck_I2c(); //發送非應位
Stop_I2c(); //結束總線
return(Val);
}
/*------------------------------------------------
數字分解
------------------------------------------------*/
void DisNumber()
{
if(ShowType == 0)
{
TempData[0]=dofly_DuanMa[value_V/100]|0x80; //采集值百位
TempData[1]=dofly_DuanMa[(value_V%100)/10]; //采集值十位
TempData[2]=dofly_DuanMa[(value_V%100)%10]; //采集值個位
TempData[3]=0;
TempData[4]=dofly_DuanMa[SetVol/100]|0x80; //目標值百位
TempData[5]=dofly_DuanMa[(SetVol%100)/10]; //目標值十位
TempData[6]=dofly_DuanMa[(SetVol%100)%10]; //目標值個位
}
if(ShowType == 1)
{
TempData[0]=dofly_DuanMa[pwmValue/1000]; //采集值千位
TempData[1]=dofly_DuanMa[(pwmValue%1000)/100]; //采集值百位
TempData[2]=dofly_DuanMa[(pwmValue%100)/10]; //采集值十位
TempData[3]=dofly_DuanMa[(pwmValue%100)%10]; //目標值個位
TempData[4]=dofly_DuanMa[TimerCount/100]; //目標值百位
TempData[5]=dofly_DuanMa[(TimerCount%100)/10]; //目標值十位
TempData[6]=dofly_DuanMa[(TimerCount%100)%10]; //目標值個位
}
}
/*------------------------------------------------
主程序
------------------------------------------------*/
main()
{
unsigned char num=0;
Init_Timer0();
Init_Timer1(); //定時器初始化
IncPIDInit(); //pid初始化
PWMOUT = 0;
LED1 =0;
LED2 =0;
LED3 =0;
while (1) //主循環
{
DisNumber();
KeyValue=KeyScan();
switch(KeyValue)
{
case Key_Change:ShowType = ~ShowType;break;
case Key_Stop:StopFlag = ~StopFlag;break;
case Key_Dec:SetVol--;break;
case Key_Add:SetVol++;break;
case Key_Add10:SetVol+=10;break;
case Key_Add100:SetVol+=100;break;
case Key_Dec10:SetVol-=10;break;
case Key_Dec100:SetVol-=100;break; //根據按鍵操作id改變目標值
default:break;
}
//主循環中添加其他需要一直工作的程序
}
}
/*------------------------------------------------
定時器中斷子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{
TH0=(65536-45872)/256; //定時時間高八位初值
TL0=(65536-45872)%256; //定時時間低八位初值
TimerCount++;
if(SetVol>500)SetVol=500; //防止數據溢出
if(SetVol<0)SetVol=0; //防止數據溢出
if(TimerCount == 20&&StopFlag == 0) //TimerCount計數20時正好1s;即ad采樣間隔為1s,保證pwm穩定輸出0.5s以上;
{
TimerCount = 0; //計數清零
PWMOUT=0; //脈沖拉低
value_V=ReadADC(0); //讀取ad數據
pwmValue = pwmValue+IncPIDCalc(value_V); //pid計算
if(pwmValue<0) pwmValue = 100;
if(pwmValue>5000) pwmValue = 4990; //設置上下限
LED3 =~LED3;
}
else
{
//等待寫入;
}
// printf("pwmValue %d value_V %d \n",pwmValue,value_V);
}
void Timer1_isr(void) interrupt 3 //定時器2中斷
{
TH1 = (65536-100)/256;
TL1 = (65536-100)%256;
Display(0,8) ;
//LED3=~LED3;
PwmCount++;
if(PwmCount >= (5000-pwmValue))
{
PWMOUT = ~PWMOUT;
PwmCount = 0;
}
}
|