_____________________
預處理
一》宏定義:
1、不帶參數:
#define 標識符 常量表達式
/*#define是宏定義命令,宏名(標識符)好習慣用大寫*/
#define NIL 0x80
2、帶參數:/*相當于小函數*/
#define 宏名(參數表) 字符串
/*不僅要時行字任串替換還要進行參數的替換,在宏定義時,宏名與帶參數的括弧之間不應該加空格,否則將空格以后的字符串都作為替代字符串的一部分,這可是很容易出錯的*/
如:#define SQ(a,b) a*b
使用:x=12;y=10;area=SQ(x,y);/*則area=12*10=120*/
二》文件包含:
#include <文件名>或#include "文件名"
/*在C中用雙引用形式更保險,在C51中常用物是尖括弧形式*/
三》條件編譯:
/*一般源程序中的所有程序行都參加編譯,但有時希望對其中一部分內容只在滿足一定條件下才進行編譯,也就是對一部分內容指定編譯的條件。*/
#if、#elif、 #else、#endif、#ifdef、#ifndef
/*選擇不同的編譯范圍,產生不同的代碼,提供通用性。*/
/*如對8051在6MHZ與12MHZ下有*/
#ifdef cpu==8051
#define FREQ 6 /*程序段*/
#else
#define FREQ 12/*程序段*/
#endif
/*這樣下面的原程序不用做任何修改便可以使用于兩種時鐘頻率的單片機系統*/
四》其他:
1、#error:捕捉不可預料的編譯條件
#if (myv!=0&&myv!=1)/*假定其值必為0或1*/
#error myv must be 1 or 0/*出錯時顯示*/
#endif
2、#pragma:用于在程序中向編譯器傳送各種編譯控制命令
#pragma 編譯命令序列
/*例:想按如下命令編譯ex.c c51 ex.c debug cod large可用:*/
#pragma DB CD LA
#pragma disable
/*禁止中斷*/
單片機C語言之二_____________________________________________________________________________________
一》數據類型:
char int long
1:unsinged 0~255 0~65535 0~4294967295
2:signed -128~127 -32768~32767 -2147483648~2147483647
指針:* 3字節
位標量: sbit
特殊功能寄存器:sfr
16位特殊功能寄存器:sfr16 占2個內存單元,0~65535
可尋址位:sbit利用他可訪問51單片機的內部RAM中的可尋址位或特殊功能寄存器中的可尋址位
sfr P0=0x80;
sbit P0_1=P0^1;
/*將P0口的口地址定義為80H,將P0.1位定義為P1_1*/
二》數據存貯類型
表1. C51數據存貯類型
━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━
數據存貯類型 ┃ 與存貯空間的對應關系
━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━
data ┃ 直接尋址片內數據存貯區,訪速度快
bdata ┃ 可位尋址片內數據存貯區,允許位與字節混合訪問
idata ┃ 間接尋址片內數據存貯區,可訪問片內全部RAM地址空間
pdata ┃ 分頁尋址片外數據存貯區(256字節)由MOVX @R0訪問
xdata ┃ 片外數據存貯區(64K),由MOVX @DPTR訪問
code ┃ 代碼存貯區(64K),由MOVC @DPTR訪問
━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━
變量的存貯類型定義:
char data var
/*字符變量var被定義為data存貯類型,C51編譯器將把該變量定位在51單片機片內數據區存貯區中*/
bit bdata flag
/*位變量flag被定義為bdata存貯類型,C51編譯器將把該變量定位在51單片機片內數據區存貯區(RAM)中的位尋址區:20H--2FH*/
三》typedef:重新定義數據類型
typedef 已有數據類型 新的數據類型
typedef int word;
/*將word定義為整型*/
word i,j;
/*將i,j定義為整型*/
四》位運算符:
━━━━┳━━━━━┳━━━━━┳━━━━━━┳━━━━━━┳━━━━━━
~ ┃ & ┃ | ┃ ^ ┃ << ┃ >>
━━━━╋━━━━━╋━━━━━╋━━━━━━╋━━━━━━╋━━━━━━
按位取反┃ 按位與 ┃ 按位或 ┃ 按位異或 ┃ 左移 ┃ 右移
━━━━┻━━━━━┻━━━━━┻━━━━━━┻━━━━━━┻━━━━━━
對移位:如<< ,a<<2,即為將二進制的a左移兩位,若a=0x8f,即10001111,a=a<<2,將導致a=0x3c(00111100),右邊補零。
五》條件運算符:
邏輯表達式? 表達式1:表達式2
六》指針與地址運算符:
*取內容 &取地址
七》強制類型轉換:(類型)=表達式
(char *)0xb000
八》sizeof 取數據類型、變量以及表達式的字節數的運算符;
九》continue:中斷語句:結束本次循環。
單片機C語言之三_____________________________________________________________________________________
函數:
一》中斷服務函數與寄存器組定義:
函數類型 函數名(形式參數表) [interrupt n][using n]
n為中斷號,0~31:
━━━━┳━━━━━┳━━━━━
中斷編號┃ 中斷向量┃ 入口地址
━━━━╋━━━━━╋━━━━━
0 ┃ 外中斷0 ┃ 0003H
━━━━╋━━━━━╋━━━━━
1 ┃ 定時器0 ┃ 000BH
━━━━╋━━━━━╋━━━━━
2 ┃ 外中斷1 ┃ 0013H
━━━━╋━━━━━╋━━━━━
3 ┃ 定時器1 ┃ 001BH
━━━━╋━━━━━╋━━━━━
4 ┃ 串行口 ┃ 0023H
━━━━┻━━━━━┻━━━━━
后面的n指的是四個工作寄存器組的一個:0~3
對函數目標代碼影響如下:
在函數入口處將當前工作寄存器組保護到堆棧中;指定的工作寄存器內容不會改變,函數返回前將被保護的工作寄存器組從堆棧中恢復!
例(定時1ms):
#include <reg51.h>
sbit P1_0=P1^0;
void timer0(void) interrupt 1 using 1{
P1_0=!P1_0;
TH0=-(1000/256);
TL0=-(1000%256);
}
main(){
SP=0x60;
P1_0=0;
TMOD=0X01;
TH0=-(1000/256);
TL0=-(1000%256);
EA=1;
ET0=1;
TR0=1;
do{}while(1);
}
/* 注意:
1、如果中斷函數中用到浮點運算,必須保存浮點寄存器的狀態。(在math.h中保存浮點寄存器函數為pfsave, 恢復浮點寄存器的狀態函數為fprestore)
2、如果在中斷函數中調用了其他函數,則被調函數所使用的工作寄存器組與中斷函數的一致!
*/
單片機C語言之四_____________________________________________________________________________________
一、 局部變量與全局變量(外部變量):
1、 全局變量若不在開頭定義則加extern
2、 全局變量會使代碼長,占用內存多
二、 存儲方式:
自動變量(auto):缺省,函數調用存在,退出消失。
內部變量 靜態變量(static):static int a=5;始終存在,退出不消失,但不能訪問。
寄存器變量(register):速度快。通常只給編譯器一個建議,由編譯器根
據實際情況確定。(見下)
變量 全局變量(global):
外部變量
靜態變量(static):
寄存器變量例:
#include<stdio.h>
int_power(m,e)
int m;
register int e;
{
register int temp;
temp=1;
for(;e;e--)
temp*=m;
return(temp);
}
main()
{
……
}
三、 函數的參數和局部變量的存儲器模式:
三種存儲器模式:small,compact,large.
一個函數的存儲器模式確定了函數的參數和局部變量在內存中的地址空間
small:內部ram
compact, large:外部RAM
函數類型 函數名(形式參數表)[存儲器模式]
例:
#pragma large /*默認存儲器模式為large*/
extern int calc(char I,int b)small; /*指定small模式*/
extern int func(int I,float f) large; /*指定large模式*/
int large_te(int I,int k) /*未指定,按默認的large模式處理*/
{
return(mtest(I,k)+2);
}
利用存儲器混合模式編程,充分利用有限的存儲空間,還可加快程序的執行速度!
單片機C語言之五_____________________________________________________________________________________
數組
1>初始化數組:
unsigned char a[5]={0x11,0x22,0x33,0x44,0x55}
或
unsigned char a[ ] ={0x11,0x22,0x33,0x44,0x55,0x66}
3>數組作為函數的參數:不但可以由變量作為函數的參數外,還可以用數組名作為函數的參數。一個數組數組名表示該數組的首地址。用一個數組名作為函數的參數時,在執行函數調用的過程中參數傳遞方式采用的是地址傳遞。將實際參數數組首地址傳遞給被調函數中的形式參數數組,這樣一來兩個數組就占有同一段內存單元。見下圖:
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
起始地址1000
b[0] b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8] b[9]
用數組名作為函數的參數,應該在主調函數和被調函數中分別進行數組定義而不能只在一方定義數組。而且在兩個函數中定義的數組類型必須一致,如果類型不一致將導致編譯出錯。實參數組和型參數組的長度可以一致可以不一致,編譯器對形參數組的長度不做檢查,直只是將實參數組的首地址傳遞給行參數組。如果希望行參數組能得到實參數組的全部元素,則應使兩個數組的長度一致。定義型參數組時可以不指定長度,只在數組名后面跟一個方括號[]。這時為了在被調函數中處理數組元素的需要,應另外設置一個參數來傳遞數組元素的個數。
例:用數組作為函數的參數,計算兩個不同長度的數組中所有元素的平均值
#include<stdio.h>
float average(array,n)
int n;
float array[ ];
{
int I;
float aver,sum=array[0];
for(I=1;I<n;I++)
sum=sum+array[I];
aver=sum/n;
return(aver);
}
main()
{
float pot_1[2]={99.9,88.8};
float pot_2[3]={11,22,33.3};
average(pot_1,2);
average(pot_1,3);
}
單片機C語言之六_____________________________________________________________________________________
軟件法去干擾:
工程上我們在采集數據時一般要求精度達到5%%,大于這個值將認為無效。我在實際應用中采用8535對32路數據進行采集(8535帶10位AD,帶看門狗),發現數據跳動有時達7%%,這是由于各種干擾造成的。主要來自于隨機干擾,下面就各種干擾的方法給出簡單的去除方法:
1、白噪聲:重要的統計特性為平均值為0,可采取每路數據采集幾次求平均的方法;
2、隨機干擾:該點明顯高于或低于附近正常采樣值,故采取中值濾波法,即對被測信號連續采樣M次,進行大小排序,取大小居中的1/3個采樣值進行算術平均;
3、電源干擾:特點是有固定周期,故可采用定時采樣求平均的方法。
由于各種排序與求平均算法用C易于實現,故C常常用于采集系統中軟件去干擾。至于排序算法可參考上一篇文章,有一個經典的程序。
在實際中我們采用每路猜9個值,排序,取中間3個,求平均。然后。。,每路數據幾乎不動!
單片機C語言之七_____________________________________________________________________________________
指針:可對內存地址直接操作
基于存貯器的指以貯器類為參量,它在編譯時才被確定。因此為指針選擇存貯器的方法可以省掉,以這些指針的長度可為1個字節(idata *,data *,pdata *)或2個這節(code *,xdata *)。
char xdata *address;
ADC0809具有8個模擬量輸入通道,采用中斷方式,在中斷函數中讀取8個通道的A/D轉換值,分別存儲在外部RAM的1000H~1007H單元。ADC0809端口地址為00F0H。
程序定義了兩個指針變量* ADC和* ADCdata,分別指向ADC0809端口地址(00F0H)和外部RAM單元地址(1000H~1007H)
由*ADC=I送入通道數,啟動ADC0809進行A/D轉換,轉換結束時產生INT1中斷。在中斷服務函數int1()中通過temp=*ADC和*ADCdata=temp;讀取A/D轉換結果并存到外部RAM中。
#include<reg51.h>
unsigned int xdata *ADC; /*定義ADC0809端口指針*/
unsigned int xdata *ADCdata; /*定義ADC0809數據緩沖器指針*/
unsigned char I;
void main( )
{
ADC=0x00f0; /*定義端口地址和數據緩沖器地址*/
ADCdata=0x1000;
I=8; /* ADC0809有8個模擬輸入通道*/
EA=1; EX1=1;IT1=1; /*開中斷*/
*ADC=I; /*啟動ADC0809*/
WHILE(I); /*等待8個通道A/D轉換完*/
}
void int1() interrupt 2
{
unsigned char tmp;
temp=*ADC; /*讀取A/D轉換結果*/
*ADCdata=temp; /*結果值存到數據緩沖區*/
ADCdata++; /*數據緩沖區地址加1*/
i—;
*ADC=I; /*啟動下一個模擬輸入通道A/D轉換*/
}
除了用指針變量來實現對內存地址的直接操作外,c51編譯器還提供一組宏,該宏定義文件為:“absacc.h”,利用它可十分方便地實現對任何內存空間的直接操作,改寫上面的程序:
#include<reg51.h>
#include<absacc.h> /*包含地址操作預定義頭文件*/
#define ADC 0x00f0; /*定義ADC0809端口地址*/
#define ADCdata 0X1000 /*定義數據緩沖器地址*/
unsigned char I;
void main( )
{
I=8; / *ADC0809有8個模擬輸入通道*/
EA=1;ex1=1;it1=1; / *開中斷*/
XBYTE[ADC]=I; /*啟動0809 */
While(i); /*等待8個通道轉換完畢*/
}
void int1() interrupt2 {
unsigned char tmp;
tmp=XBYTE[ADC]; /*讀取A/D轉換結果*/
i--;
XBYTE[ADCdata+I]=tmp; /**結果值存儲到數據緩沖器*/
XBYTE[ADC]=I; /*啟動下一個模擬輸入通道A/D轉換*/
}
兩指針相減-----計算字符串的長度
#include<stdio.h>
main() {
char *s=”abcdef”;
int strlen(char *s);
printf(“\n length of ‘%%s’=%%d\n”,s,strlen(s));
}
int strlen(char *s) {
char *p=s;
while(*p!=’\0’)p++;
return(p-s);
}
結果為:length of ‘abcdef’=6
注:不允許指針之間進行加,乘,除,移位,或屏蔽運算,也不允許用float類型數據與指針做加,減運算!
抽象型指針:
ANSI新標準增加了一種“void * ”的指針類型,這是一種抽象型指針,即可以定義一個指針變量,但不指定該指針是指向哪種類型的數據的。在賦值時需進行強制類型轉換:
Char *p1;
Void *p2;
P1=(char*)p2;
抽象型指針可以用來在每個存儲區內訪問任意地址或者用來產生調用。