导航:首页 > 源码编译 > 单片机测量与控制算法

单片机测量与控制算法

发布时间:2024-08-08 00:03:17

① crc算法单片机上的实现

CRC算法原理及C语言实现

摘 要 本文从理论上推导出CRC算法实现原理,给出三种分别适应不同计算机或微控制器硬件环境的C语言程序。读者更能根据本算法原理,用不同的语言编写出独特风格更加实用的CRC计算程序。
关键词 CRC 算法 C语言
1 引言
循环冗余码CRC检验技术广泛应用于测控及通信领域。CRC计算可以靠专用的硬件来实现,但是对于低成本的微控制器系统,在没有硬件支持下实现CRC检验,关键的问题就是如何通过软件来完成CRC计算,也就是CRC算法的问题。
这里将提供三种算法,它们稍有不同,一种适用于程序空间十分苛刻但CRC计算速度要求不高的微控制器系统,另一种适用于程序空间较大且CRC计算速度要求较高的计算机或微控制器系统,最后一种是适用于程序空间不太大,且CRC计算速度又不可以太慢的微控制器系统。
2 CRC简介
CRC校验的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去。在接收端,则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。
16位的CRC码产生的规则是先将要发送的二进制序列数左移16位(既乘以 )后,再除以一个多项式,最后所得到的余数既是CRC码,如式(2-1)式所示,其中B(X)表示n位的二进制序列数,G(X)为多项式,Q(X)为整数,R(X)是余数(既CRC码)。
(2-1)
求CRC码所采用模2加减运算法则,既是不带进位和借位的按位加减,这种加减运算实际上就是逻辑上的异或运算,加法和减法等价,乘法和除法运算与普通代数式的乘除法运算是一样,符合同样的规律。生成CRC码的多项式如下,其中CRC-16和CRC-CCITT产生16位的CRC码,而CRC-32则产生的是32位的CRC码。本文不讨论32位的CRC算法,有兴趣的朋友可以根据本文的思路自己去推导计算方法。
CRC-16:(美国二进制同步系统中采用)
CRC-CCITT:(由欧洲CCITT推荐)
CRC-32:

接收方将接收到的二进制序列数(包括信息码和CRC码)除以多项式,如果余数为0,则说明传输中无错误发生,否则说明传输有误,关于其原理这里不再多述。用软件计算CRC码时,接收方可以将接收到的信息码求CRC码,比较结果和接收到的CRC码是否相同。

3 按位计算CRC
对于一个二进制序列数可以表示为式(3-1):
(3-1)
求此二进制序列数的CRC码时,先乘以 后(既左移16位),再除以多项式G(X),所得的余数既是所要求的CRC码。如式(3-2)所示:
(3-2)
可以设: (3-3)
其中 为整数, 为16位二进制余数。将式(3-3)代入式(3-2)得:

(3-4)
再设: (3-5)
其中 为整数, 为16位二进制余数,将式(3-5)代入式(3-4),如上类推,最后得到:
(3-6)
根据CRC的定义,很显然,十六位二进制数 既是我们要求的CRC码。
式(3-5)是编程计算CRC的关键,它说明计算本位后的CRC码等于上一位CRC码乘以2后除以多项式,所得的余数再加上本位值除以多项式所得的余数。由此不难理解下面求CRC码的C语言程序。*ptr指向发送缓冲区的首字节,len是要发送的总字节数,0x1021与多项式有关。
unsigned int cal_crc(unsigned char *ptr, unsigned char len) {
unsigned char i;
unsigned int crc=0;
while(len--!=0) {
for(i=0x80; i!=0; i/=2) {
if((crc&;0x8000)!=0) {crc*=2; crc^=0x1021;} /* 余式CRC乘以2再求CRC */
else crc*=2;
if((*ptr&;i)!=0) crc^=0x1021; /* 再加上本位的CRC */
}
ptr++;
}
return(crc);
}
按位计算CRC虽然代码简单,所占用的内存比较少,但其最大的缺点就是一位一位地计算会占用很多的处理器处理时间,尤其在高速通讯的场合,这个缺点更是不可容忍。因此下面再介绍一种按字节查表快速计算CRC的方法。
4 按字节计算CRC
不难理解,对于一个二进制序列数可以按字节表示为式(4-1),其中 为一个字节(共8位)。
(4-1)
求此二进制序列数的CRC码时,先乘以 后(既左移16位),再除以多项式G(X),所得的余数既是所要求的CRC码。如式(4-2)所示:
(4-2)
可以设: (4-3)
其中 为整数, 为16位二进制余数。将式(4-3)代入式(4-2)得:
(4-4)
因为:
(4-5)
其中 是 的高八位, 是 的低八位。将式(4-5)代入式(4-4),经整理后得:
(4-6)
再设: (4-7)
其中 为整数, 为16位二进制余数。将式(4-7)代入式(4-6),如上类推,最后得:
(4-8)
很显然,十六位二进制数 既是我们要求的CRC码。
式(4-7)是编写按字节计算CRC程序的关键,它说明计算本字节后的CRC码等于上一字节余式CRC码的低8位左移8位后,再加上上一字节CRC右移8位(也既取高8位)和本字节之和后所求得的CRC码,如果我们把8位二进制序列数的CRC全部计算出来,放如一个表里,采用查表法,可以大大提高计算速度。由此不难理解下面按字节求CRC码的C语言程序。*ptr指向发送缓冲区的首字节,len是要发送的总字节数,CRC余式表是按0x11021多项式求出的。
unsigned int cal_crc(unsigned char *ptr, unsigned char len) {
unsigned int crc;
unsigned char da;
unsigned int crc_ta[256]={ /* CRC余式表 */
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x 1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};

crc=0;
while(len--!=0) {
da=(uchar) (crc/256); /* 以8位二进制数的形式暂存CRC的高8位 */
crc<<=8; /* 左移8位,相当于CRC的低8位乘以 */
crc^=crc_ta[da^*ptr]; /* 高8位和当前字节相加后再查表求CRC ,再加上以前的CRC */
ptr++;
}
return(crc);
}
很显然,按字节求CRC时,由于采用了查表法,大大提高了计算速度。但对于广泛运用的8位微处理器,代码空间有限,对于要求256个CRC余式表(共512字节的内存)已经显得捉襟见肘了,但CRC的计算速度又不可以太慢,因此再介绍下面一种按半字节求CRC的算法。
5 按半字节计算CRC
同样道理,对于一个二进制序列数可以按字节表示为式(5-1),其中 为半个字节(共4位)。
(5-1)
求此二进制序列数的CRC码时,先乘以 后(既左移16位),再除以多项式G(X),所得的余数既是所要求的CRC码。如式(4-2)所示:
(5-2)
可以设: (5-3)
其中 为整数, 为16位二进制余数。将式(5-3)代入式(5-2)得:
(5-4)
因为:
(5-5)
其中 是 的高4位, 是 的低12位。将式(5-5)代入式(5-4),经整理后得:
(5-6)
再设: (5-7)
其中 为整数, 为16位二进制余数。将式(5-7)代入式(5-6),如上类推,最后得:
(5-8)
很显然,十六位二进制数 既是我们要求的CRC码。
式(5-7)是编写按字节计算CRC程序的关键,它说明计算本字节后的CRC码等于上一字节CRC码的低12位左移4位后,再加上上一字节余式CRC右移4位(也既取高4位)和本字节之和后所求得的CRC码,如果我们把4位二进制序列数的CRC全部计算出来,放在一个表里,采用查表法,每个字节算两次(半字节算一次),可以在速度和内存空间取得均衡。由此不难理解下面按半字节求CRC码的C语言程序。*ptr指向发送缓冲区的首字节,len是要发送的总字节数,CRC余式表是按0x11021多项式求出的。
unsigned cal_crc(unsigned char *ptr, unsigned char len) {
unsigned int crc;
unsigned char da;
unsigned int crc_ta[16]={ /* CRC余式表 */
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
}

crc=0;
while(len--!=0) {
da=((uchar)(crc/256))/16; /* 暂存CRC的高四位 */
crc<<=4; /* CRC右移4位,相当于取CRC的低12位)*/
crc^=crc_ta[da^(*ptr/16)]; /* CRC的高4位和本字节的前半字节相加后查表计算CRC,
然后加上上一次CRC的余数 */
da=((uchar)(crc/256))/16; /* 暂存CRC的高4位 */
crc<<=4; /* CRC右移4位, 相当于CRC的低12位) */
crc^=crc_ta[da^(*ptr&;0x0f)]; /* CRC的高4位和本字节的后半字节相加后查表计算CRC,
然后再加上上一次CRC的余数 */
ptr++;
}
return(crc);
}

② 单片机pid算法控制步进电机的电路图和程序

//P1.1(T0):Count They Distance
//P0.4:Tx
//P0.5:Rx
#include <C8051F310.h> //SFR declarations
#include <stdio.h> //Standard I/O definition file
#include <math.h> //Math library file
#include <Intrins.h>
#include <absacc.h>

unsigned int j,i;
char a=0;
unsigned int t=0;

//sbit led=P0^2;
//P0.0(PWM0):给定左轮速度.
sbit vls=P0^4; //P0.4(GPIO):给定左轮方向.
sbit vlf=P0^6; //P0.6(T0) :反馈左轮速度.
sbit dlf=P1^0; //P1.0(GPIO):反馈左轮方向.

//P0.2(PWM0):给定右轮速度.
sbit vrs=P0^5; //P0.5(GPIO):给定右轮方向.
sbit vrf=P0^7; //P0.7(T0) :反馈右轮速度.
sbit drf=P1^1; //P1.1(GPIO):反馈右轮方向.

int ol; //左轮给定值
int len;
int len_1,len_2;
int lyn_1,lyn_2;
int vl1,vl2; //反馈左轮速度值(取样周期内的方波数)
int lfz; //运算后赋给PWM的值

int lyn,lynn;
int lun=0,lun_1=0; //偏差校正值 即校正PWM输出
int lunp,luni,lund; //PID 校正值

int or; //右轮给定值
int ren;
int ren_1,ren_2;
int ryn_1,ryn_2;
int vr1,vr2; //反馈右轮速度值(取样周期内的方波数)
int rfz; //运算后赋给PWM的值

int ryn,rynn;
int run=0,run_1=0; //偏差校正值 即校正PWM输出
int runp,runi,rund; //PID 校正值

float kp=2.0; //比例系数1.8
float kd=0.2; //微分系数0.4
float lki; //积分系数

void pio_init(void);
void sys_init(void);
void t01_init(void);
void TIME3_INT(void);
void PID(void);
void interrupt_init(void);
void delay(unsigned int x);
void pwm1_1(void);

void main(void)
{
PCA0MD &= ~0x40; //关闭
pio_init(); //P11为测距输入端
sys_init();
t01_init();
pwm1_1();
TIME3_INT();
interrupt_init();

vls=1;vrs=0;
while(1)
{

ol=50;
or=50;
delay(1000);

ol=100;
or=100;
delay(1000);

ol=-50;
or=50;
delay(1000);

}

}

void PID(void)
{
/****************左轮PID调节******************/
if(dlf==1)
{
lyn=(vl2*256+vl1); //dlf是左轮反馈方向,0表示向前 vl=TL0
}
else
{
lyn=-(vl2*256+vl1); //dlf=1表示是向后退,速度应该为负值
}

len=ol-lyn; //误差=给定速度-反馈速度(取样周期内的方波数)

if(abs(len)<8)//30
{
lki=1.4; //ki值的确定1.4
}
else
{
lki=0.05; //积分系数:如果 | 给定值-反馈值 | 太大
} //则就可以不引入积分,或者引入的很小0.05

lunp=kp*(len-len_1); //比例校正
luni=lki*len; //积分校正
lund=kd*(len-2*len_1+len_2); //微分校正

lun=lunp+luni+lund+lun_1; //总校正

/*************新旧数据更新*************************/
len_2=len_1;
len_1=len; //len:当前取样周期内出现的速度偏差;len_1:上次取样周期内出现的速度偏差
lun_1=lun; //lun:当前取样周期内得出的PWM校正值;lun_1:上次取样周期内得出的PWM校正值
/*************新旧数据更新*************************/

if(lun>255)
{
lun=255; //正速度
}
if(lun<-255)
{
lun=-255; //负速度
}
if(lun<0)

{
vls=1;
PCA0CPH0=-lun;
}

if(lun>=0)
{
vls=0;
PCA0CPH0=lun;
}

/****************右轮PID调节******************/
if(drf==0)
{
ryn=(vr2*256+vr1); //drf是右轮反馈方向,0表示向前 vl=TL0
}
else
{
ryn=-(vr2*256+vr1); //dlf=1表示是向后退,速度应该为负值
}

ren=or-ryn; //误差=给定速度-反馈速度(取样周期内的方波数)

if(abs(ren)<8)//30
{
lki=1.4; //ki值的确定1.4
}
else
{
lki=0.05; //积分系数:如果 | 给定值-反馈值 | 太大
} //则就可以不引入积分,或者引入的很小0.05

runp=kp*(ren-ren_1); //比例校正
runi=lki*ren; //积分校正
rund=kd*(ren-2*ren_1+ren_2); //微分校正

run=runp+runi+rund+run_1; //总校正

/*************新旧数据更新*************************/
ren_2=ren_1;
ren_1=ren; //len:当前取样周期内出现的速度偏差;len_1:上次取样周期内出现的速度偏差
run_1=run; //lun:当前取样周期内得出的PWM校正值;lun_1:上次取样周期内得出的PWM校正值
/*************新旧数据更新*************************/

if(run>255)
{
run=255; //正速度
}
if(run<-255)
{
run=-255; //负速度
}
if(run<0)

{
vrs=1;
PCA0CPH1=-run;
}

if(run>=0)
{
vrs=0;
PCA0CPH1=run;
}
//因为这里的PCA0CPH0越大,对应的电机速度越小,所以要255来减一下
}

void pio_init(void)
{
XBR0=0x00; //0000 0001
XBR1=0x72; //0111 0010 时能弱上拉 T0T1连接到脚口P06、P07 CEX0、CEX1连接到脚口P00、P01

P0MDIN=0xff; //模拟(0);数字(1) 1111 0011
P0MDOUT=0xc3;//开漏(0);推挽(1) 1111 1111
P0SKIP=0x3c; //0011 1100

P1MDIN=0xff; //1111 1111
P1MDOUT=0xfc;//
P1SKIP=0x00; //1111 1111

}

void sys_init(void) //12MHz
{
OSCICL=0x43;
OSCICN=0xc2;
CLKSEL=0x00;

}

void pwm1_1(void) //PWM的初始化
{
PCA0MD=0x08; //PCA时钟为12分频

PCA0CPL0=200; //左轮
PCA0CPM0=0x42; //设置左轮为8位PWM输出
PCA0CPH0=200;

PCA0CPL1=200; //平衡校正
PCA0CPM1=0x42; //设置为8位PWM输出
PCA0CPH1=200;

PCA0CN=0x40; //允许PCA工作
}

void t01_init(void)
{
TCON=0x50; //计数器1、2允许
TMOD=0x55; //定时器1、2采用16位计数功能
CKCON=0x00;

TH1=0x00; //用于采集左轮的速度
TL1=0x00;

TH0=0x00; //用于采集右轮的速度
TL0=0x00;
}

void TIME3_INT(void)
{
TMR3CN = 0x00; //定时器3为16位自动重载
CKCON &= ~0x40;

TMR3RLL = 0xff;
TMR3RLH = 0xd7;
TMR3L = 0xff;
TMR3H = 0xd7;

TMR3CN |= 0x04;
}

void T3_ISR() interrupt 14 //定时器3中断服务程序
{
//led=~led;
EA=0;
TCON &=~0x50; //关闭计数器0、1

vl1=TL0; //取左轮速度值
vl2=TH0;

vr1=TL1; //取右轮速度值
vr2=TH1;

TH1=0x00;
TL1=0x00;

TH0=0x00;
TL0=0x00;

PID(); //PID处理

TMR3CN &=~0x80; //清中断标志位
TCON |=0x50; //重新开计数器0、1
EA=1;
}
void interrupt_init(void)
{ IE=0x80;
IP=0x00;
EIE1|=0x80;
EIP1|=0x80;

}

void delay(unsigned int m) //延时程序
{
for(i=0;i<2000;i++)
{
for(j=0;j<m;j++){_nop_(); _nop_();}
}
}

③ 求助单片机练习题。第四题的算法应该是怎么样的。详细点

图中,公式,应该是:
2 × ((1 + 2) × 200 + 1 + 2) = 1206 微秒。

④ 用单片机做PID算法控制问题

1.可以直接套用PID公式,无论增量还是绝对的。PID算法是根据误差来控制的算法,不依赖系统的模型,故不用算系统的传递函数。有的书提到传递函数,一般是用于理论建模仿真,从而直接用Matlab一类的仿真软件进行PID参数调试。得到的参数可以为实际应用提供一定参考价值。

2.PID参数整定有一套原则。首先要了解各个参数的作用。具体的整定方法,随便找本自控原理的书都会提到,我不太记得了,大致是有一个倍数关系。但实际操作,一般不会是用这个数,是需要根据系统的反应,改变各个参数来试的。尽信书不如无书啊~
另外,不同系统的参数肯定不一样。就算同一个系统,稍微有一些改变,可能最好的那组参数就会变化。因此衍生了很多先进PID算法,如神经PID、专家PID、模糊PID等等。

⑤ 8位单片机PID控制PWM的算法如何实现,C语言计算

PID控制在8位单片机中仍然有广泛的应用,比如温度控制,利用比例、积分、微分补偿来做恒温补偿控制,当然由于有这些数学处理,用C语言相对方便一些,以下是一个具体的实例。

#include<reg51.h>

#include<intrins.h>

#include<math.h>

#include<string.h>

struct PID {

unsigned int SetPoint; // 设定目标 Desired Value

unsigned int Proportion; // 比例常数 Proportional Const

unsigned int Integral; // 积分常数 Integral Const

unsigned int Derivative; // 微分常数 Derivative Const

unsigned int LastError; // Error[-1]

unsigned int PrevError; // Error[-2]

unsigned int SumError; // Sums of Errors

};

struct PID spid; // PID Control Structure

unsigned int rout; // PID Response (Output)

unsigned int rin; // PID Feedback (Input)

sbit data1=P1^0;

sbit clk=P1^1;

sbit plus=P2^0;

sbit subs=P2^1;

sbit stop=P2^2;

sbit output=P3^4;

sbit DQ=P3^3;

unsigned char flag,flag_1=0;

unsigned char high_time,low_time,count=0;//占空比调节参数

unsigned char set_temper=35;

unsigned char temper;

unsigned char i;

unsigned char j=0;

unsigned int s;

/***********************************************************

延时子程序,延时时间以12M晶振为准,延时时间为30us×time

***********************************************************/

void delay(unsigned char time)

{

unsigned char m,n;

for(n=0;n<time;n++)

for(m=0;m<2;m++){}

}

/***********************************************************

写一位数据子程序

***********************************************************/

void write_bit(unsigned char bitval)

{

EA=0;

DQ=0; /*拉低DQ以开始一个写时序*/

if(bitval==1)

{

_nop_();

DQ=1; /*如要写1,则将总线置高*/

}

delay(5); /*延时90us供DA18B20采样*/

DQ=1; /*释放DQ总线*/

_nop_();

_nop_();

EA=1;

}

/***********************************************************

写一字节数据子程序

***********************************************************/

void write_byte(unsigned char val)

{

unsigned char i;

unsigned char temp;

EA=0;

TR0=0;

for(i=0;i<8;i++) /*写一字节数据,一次写一位*/

{

temp=val>>i; /*移位操作,将本次要写的位移到最低位*/

temp=temp&1;

write_bit(temp); /*向总线写该位*/

}

delay(7); /*延时120us后*/

// TR0=1;

EA=1;

}

/***********************************************************

读一位数据子程序

***********************************************************/

unsigned char read_bit()

{

unsigned char i,value_bit;

EA=0;

DQ=0; /*拉低DQ,开始读时序*/

_nop_();

_nop_();

DQ=1; /*释放总线*/

for(i=0;i<2;i++){}

value_bit=DQ;

EA=1;

return(value_bit);

}

/***********************************************************

读一字节数据子程序

***********************************************************/

unsigned char read_byte()

{

unsigned char i,value=0;

EA=0;

for(i=0;i<8;i++)

{

if(read_bit()) /*读一字节数据,一个时序中读一次,并作移位处理*/

value|=0x01<<i;

delay(4); /*延时80us以完成此次都时序,之后再读下一数据*/

}

EA=1;

return(value);

}

/***********************************************************

复位子程序

***********************************************************/

unsigned char reset()

{

unsigned char presence;

EA=0;

DQ=0; /*拉低DQ总线开始复位*/

delay(30); /*保持低电平480us*/

DQ=1; /*释放总线*/

delay(3);

presence=DQ; /*获取应答信号*/

delay(28); /*延时以完成整个时序*/

EA=1;

return(presence); /*返回应答信号,有芯片应答返回0,无芯片则返回1*/

}

/***********************************************************

获取温度子程序

***********************************************************/

void get_temper()

{

unsigned char i,j;

do

{

i=reset(); /*复位*/

} while(i!=0); /*1为无反馈信号*/

i=0xcc; /*发送设备定位命令*/

write_byte(i);

i=0x44; /*发送开始转换命令*/

write_byte(i);

delay(180); /*延时*/

do

{

i=reset(); /*复位*/

} while(i!=0);

i=0xcc; /*设备定位*/

write_byte(i);

i=0xbe; /*读出缓冲区内容*/

write_byte(i);

j=read_byte();

i=read_byte();

i=(i<<4)&0x7f;

s=(unsigned int)(j&0x0f); //得到小数部分

s=(s*100)/16;

j=j>>4;

temper=i|j; /*获取的温度放在temper中*/

}

/*====================================================================================================

Initialize PID Structure

=====================================================================================================*/

void PIDInit (struct PID *pp)

{

memset ( pp,0,sizeof(struct PID)); //全部初始化为0

}

/*====================================================================================================

PID计算部分

=====================================================================================================*/

unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint )

{

unsigned int dError,Error;

Error = pp->SetPoint - NextPoint; // 偏差

pp->SumError += Error; // 积分

dError = pp->LastError - pp->PrevError; // 当前微分

pp->PrevError = pp->LastError;

pp->LastError = Error;

return (pp->Proportion * Error // 比例项

+ pp->Integral * pp->SumError // 积分项

+ pp->Derivative * dError); // 微分项

}

/***********************************************************

温度比较处理子程序

***********************************************************/

void compare_temper()

{

unsigned char i;

if(set_temper>temper) //是否设置的温度大于实际温度

{

if(set_temper-temper>1) //设置的温度比实际的温度是否是大于1度

{

high_time=100; //如果是,则全速加热

low_time=0;

}

else //如果是在1度范围内,则运行PID计算

{

for(i=0;i<10;i++)

{

get_temper(); //获取温度

rin = s; // Read Input

rout = PIDCalc ( &spid,rin ); // Perform PID Interation

}

if (high_time<=100)

high_time=(unsigned char)(rout/800);

else

high_time=100;

low_time= (100-high_time);

}

}

else if(set_temper<=temper)

{

if(temper-set_temper>0)

{

high_time=0;

low_time=100;

}

else

{

for(i=0;i<10;i++)

{

get_temper();

rin = s; // Read Input

rout = PIDCalc ( &spid,rin ); // Perform PID Interation

}

if (high_time<100)

high_time=(unsigned char)(rout/10000);

else

high_time=0;

low_time= (100-high_time);

}

}

// else

// {}

}

/*****************************************************

T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期

******************************************************/

void serve_T0() interrupt 1 using 1

{

if(++count<=(high_time))

output=1;

else if(count<=100)

{

output=0;

}

else

count=0;

TH0=0x2f;

TL0=0xe0;

}

/*****************************************************

串行口中断服务程序,用于上位机通讯

******************************************************/

void serve_sio() interrupt 4 using 2

{

/* EA=0;

RI=0;

i=SBUF;

if(i==2)

{

while(RI==0){}

RI=0;

set_temper=SBUF;

SBUF=0x02;

while(TI==0){}

TI=0;

}

else if(i==3)

{

TI=0;

SBUF=temper;

while(TI==0){}

TI=0;

}

EA=1; */

}

void disp_1(unsigned char disp_num1[6])

{

unsigned char n,a,m;

for(n=0;n<6;n++)

{

// k=disp_num1[n];

for(a=0;a<8;a++)

{

clk=0;

m=(disp_num1[n]&1);

disp_num1[n]=disp_num1[n]>>1;

if(m==1)

data1=1;

else

data1=0;

_nop_();

clk=1;

_nop_();

}

}

}

/*****************************************************

显示子程序

功能:将占空比温度转化为单个字符,显示占空比和测得到的温度

******************************************************/

void display()

{

unsigned char code number[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};

unsigned char disp_num[6];

unsigned int k,k1;


k=high_time;

k=k%1000;

k1=k/100;

if(k1==0)

disp_num[0]=0;

else

disp_num[0]=0x60;

k=k%100;

disp_num[1]=number[k/10];

disp_num[2]=number[k%10];

k=temper;

k=k%100;

disp_num[3]=number[k/10];

disp_num[4]=number[k%10]+1;

disp_num[5]=number[s/10];

disp_1(disp_num);

}

/***********************************************************

主程序

***********************************************************/

void main()

{

unsigned char z;

unsigned char a,b,flag_2=1,count1=0;

unsigned char phil[]={2,0xce,0x6e,0x60,0x1c,2};

TMOD=0x21;

TH0=0x2f;

TL0=0x40;

SCON=0x50;

PCON=0x00;

TH1=0xfd;

TL1=0xfd;

PS=1;

EA=1;

EX1=0;

ET0=1;

ES=1;

TR0=1;

TR1=1;

high_time=50;

low_time=50;

PIDInit ( &spid ); // Initialize Structure

spid.Proportion = 10; // Set PID Coefficients 比例常数 Proportional Const

spid.Integral = 8; //积分常数 Integral Const

spid.Derivative =6; //微分常数 Derivative Const

spid.SetPoint = 100; // Set PID Setpoint 设定目标 Desired Value

while(1)

{

if(plus==0)

{

EA=0;

for(a=0;a<5;a++)

for(b=0;b<102;b++){}

if(plus==0)

{

set_temper++;

flag=0;

}

}

else if(subs==0)

{

for(a=0;a<5;a++)

for(b=0;a<102;b++){}

if(subs==0)

{

set_temper--;

flag=0;

}

}

else if(stop==0)

{

for(a=0;a<5;a++)

for(b=0;b<102;b++){}

if(stop==0)

{

flag=0;

break;

}

EA=1;

}

get_temper();

b=temper;

if(flag_2==1)

a=b;

if((abs(a-b))>5)

temper=a;

else

temper=b;

a=temper;

flag_2=0;

if(++count1>30)

{

display();

count1=0;

}

compare_temper();

}

TR0=0;

z=1;

while(1)

{

EA=0;

if(stop==0)

{

for(a=0;a<5;a++)

for(b=0;b<102;b++){}

if(stop==0)

disp_1(phil);

// break;

}

EA=1;

}

}

⑥ 单片机如何测量波形的频率和占空比

1、首先要确定波形是否有毛刺等干扰?
2、其次要确定波形的频率大概范围?
3、还要确定波形是周期波?还是任意波?
只有知道这些基本的参数,才可以进行单片机的选型、晶振选型、软件定时中断的周期选择,然后给出相应的思路和方法。
如果波形比较理想,没有毛刺,测频率一般采取过零点;
如果波形有毛刺,可以考虑先把毛刺过滤掉,过滤电路或软件视具体情况而定。
如果频率比较高,可以设置一个较长定时,比如100ms,500ms,1s等,根据波形频率选择,然后在此定时内,统计有多少个过零点,进而得出频率;
如果频率比较低,可以设置一个计数器,统计波形两次过零点之间的计数器的计数值,进而得出频率。
占空比思路和上面类似,根据统计幅度为0的时长,以及非0的时长,进而求出占空比。

⑦ 单片机中的PID算法是什么意思啊,有什么用途呢谢谢!

pid就是比例积分微分算法

⑧ 51单片机的温度控制系统一定要用到pid算法吗 还有其它方法吗

51单片机的温度控制系统中,比较好的控制算法就pid算法,这是自动控制原理中的经典算法,其它算法控制过程不够理想,过于简单,温度变化波动较大。

阅读全文

与单片机测量与控制算法相关的资料

热点内容
程序员到公司当领导 浏览:225
用算法控制玩家的行为 浏览:482
androidsdk17下载 浏览:792
怎么给单独表格添加密码 浏览:12
下载压缩密码 浏览:259
android系统上编程 浏览:469
单片机模拟i2c从机 浏览:237
教育年报系统服务器如何开启 浏览:840
对称密钥加密后的长度 浏览:292
微制造编程软件下载 浏览:107
旋住宿酒店用哪个App最好 浏览:60
三菱编程中怎么创建子程序 浏览:199
在单片机温度输入采集信号有 浏览:685
电脑云服务器同步 浏览:418
方舟生存进化手游版怎么转服务器 浏览:90
哪个app可以听小说 浏览:160
网络发送数据如何加密 浏览:201
教材完全解读pdf 浏览:820
什么是多台服务器 浏览:36
菜鸟音乐编辑app哪个好 浏览:548