1. 學習MCS-51(具體以89C51為例)系列單片機的體會
http://www.38xian.com/index.aspx?menuid=4&type=articleinfo&lanmuid=19&infoid=685&language=cn
http://www.38xian.com/index.aspx?menuid=4&type=articleinfo&lanmuid=19&infoid=686&language=cn
又來看了一下,修改一下吧,加點文字
C語言學習總結
搞嵌入式的,大都用C語言寫代碼,本人從事單片機開發,也寫了不少的代碼,一直習慣用 if 、switch打天下,在定義數據結構的時候也只用到 字元型、整型、數組,位;很少用結構體,共用體,枚舉,因為咱C語言學得不好,和它們不熟,總感覺它們不那麼好招呼,重要的是自已覺得沒必要用上它們。隨著越來越多的積累,咱寫代碼的風格也在不斷的發生變化,從以前的喜歡將所有的函數及數據的定義寫在一個文件里到逐漸的將函數按功能模塊化、從以前的習慣直接在程序里寫常數到慢慢的開始用上宏來代替,咱編程的風格也逐漸開始正規化,編程水平也逐步提升,當然這些成績都源於咱不斷的學習,學習匠人的編程規范、學習herald的感悟設計、還有網上寫得非常出色的代碼以及STM32的固件函數庫,在咱的不斷領悟和思考下,總結了幾點關於C語言的用法,與大家共同分享。
一、 學習匠人的頭文件包含巧妙用法
當一個頭文件被多個C文件包含,且該頭文件中定義了這些C文件的公共變數,則在編譯的時候會出現重復定義,導致編譯通不過,通常我們會採用如下兩種做法來解決上述問題。
(為了讓問題表述得更清楚,我們假設兩個C文件C1,C2,C3,一個頭文件H1,C1,C2,C3有兩個公共變數V1和V2)
1、 在C1文件中定義變數V1和V2,在C2和C3文件中對V1,V2用extern聲明;
2、 在C1文件中定義變數V1和V2,在H1中對V1,V2用extern聲明,然後在C2和C3文件中包含H1;
很顯然,以上兩種方法都要對V1和V2書寫至少兩次,一次定義,一次外部聲明,且不是在同一文件下,這樣不利於管理和修改,有沒有一種方法可以讓這些公用的變數放在一個文件里,且只要書寫一次呢?
偶在二姨那裡無意中看到匠人的發帖,就是關於該問題的討論,現在我轉發一下,與大家同共分享。首先我們將要用到的公共變數全部書寫到com.h文件中,每一個變數在定義前加一個符號EXT_,當該頭文件被main.c函數包含時,定義EXT_為空,表示com.h中的變數在main.c中被定義,當被其它文件包含時,定義EXT_為extern,表示外部聲明,如:
Com.h文件:
//避免重復定義
#ifdef root
#define EXT_
#else
#define EXT_ extern
#endif
//全局變數
EXT_ u8 variable1; //該變數在三個C文件中都要用到
Main.c
#define root //在包含com.h前定義root
#include "com.h"
二、 用結構體的方式來定義匯流排或外設地址
當一個整體包含不同類型的多個成員時,通常用結構體來定義結構體變數,這樣內存會將這些變數按照遞增的方式分配到相鄰的地址(不對齊的地方會有填充),按「結構體名.成員名」的方式訪問結構體內的成員,這是訪問結構體變數的方式;但是還有一種指向結構體變數的指針,它可以將某個地址轉換成該結構體類型的指針,比如寄存器的定義:
(以下是摘自STM32固件函數庫,關於GPIO的定義)
typedef struct
{
vu32 CRL; //0
vu32 CRH; //偏移量4
vu32 IDR; //偏移量8
vu32 ODR;
vu32 BSRR;
vu32 BRR;
vu32 LCKR;
} GPIO_TypeDef;
#define GPIOA_BASE ((u32)0x40010800) //GPIOA的基地址為0x40010800
#define GPIOA (GPIO_TypeDef *) GPIOA_BASE; //強制類型轉換為GPIO_TypeDef類型的指針
這樣在操作GPIOA的寄存器時只要這樣寫就可以了
讀: X="GPIOA-">CRL; 寫:GPIOA->CRL=X;
或 讀: X=(*GPIOA).CRL; 寫:(*GPIOA).CRL =X;
當然,要達到上述目的也可以採用如下方式
#define GPIOA_ CRL 0x40010800
#define GPIOA_ CRH 0x40010804
#define GPIOA_ IDR 0x40010808
#define GPIOA_ ODR 0x4001080C
#define GPIOA_ BSSR 0x40010810
#define GPIOA_ LCKR 0x40010814
很明顯,第一種書寫方式更加正規化,且當定義多個GPIO時,只要將其它GPIO的基地址強制轉換為該結構類型的指針即可。
再來看看一個定義外部匯流排的例子
typedef struct
{
vu8 CH375_DATA;
vu8 CH375_CMD; //偏移量1
} CH375_TypeDef;
#define CH375 ((CH375_TypeDef *) 0x6c000000)
CH375-> CH375_DATA=data; //往0x6c000000地址處寫數據
CH375-> CH375_CMD=cmd; //往0x6c000001地址處寫命令
怎麼樣,是不是方便多了。重要的是代碼的觀賞和可讀性提高了。
三、 用枚舉數據類型來定義特定的狀態
在實際問題中,有些變數的取值被限定在一個有限的范圍內。例如,一個函數在操作過程中會返回幾個特定的狀態:操作成功,操作失敗,忙,等等。如果我們直接在函數里用0,1和2來表示這三種狀態,有時偶爾會出現數值與實際狀態對不上號的情況,造成置狀態和判斷狀態錯誤,那麼我們可以在程序里用宏或者枚舉來事先定義好這些狀態。
如:用宏定義:
#define Sucess 0
#define Failure 1
#define Busy 2
用枚舉
typedef enum { Sucess = 0, Failure , Busy } FlagStatus;
四、 用共用體類型定義共享內存空間
共用體類型定義的數據是將多個成員共享同一內存空間,該空間的大小為最大成員的大小,其用法與結構體完全相同,但值得注意的是不能同時引用多個成員,在某一時刻只能使用其中之一成員。
在程序中如果全局變數比較多,包含幾個結構和數組,如果這些全部定義的話勢必會佔大量的內存,有可能還會導致單片機內存不夠,如果能讓幾個不同時用到的數組和結構變數共享一段內存,則能省出很多的內存空間。
比如以下輸入輸出若不同時進行,則可以共享同一段內存空間
union {
struct {
unsigned char Flag;
unsigned char Type;
unsigned char State;
unsigned long DataLen;
unsigned char Buffer[64];
}DataOut;
struct {
unsigned char Flag;
unsigned char Type;
unsigned char State;
unsigned long DataLen;
unsigned char Buffer[64];
} DataIn;
} BOC;
C語言博大精深,豐富多彩,用得好能很好的發揮它的作用,同時學習好的編程方法養成良好的編程習慣對於一名設計人員來說也是極其的重要,以上四點都是本人自身積累和學習的一些總結,希望能夠與大家一起共同交流,共同學習和提高。
個人珍藏的好文章,貼出來分享
2. 總結出51單片機工程的創建步驟
可能有些剛接觸51單片機開發的新手們,對於如何用keil創建一個項目感到無從下手,那麼現在就示範一下如何用keil得51單片機項目
3. 51,PIC,AVR單片機它們的優點缺點都有哪些
51單片機,是嵌入式系統的起源。歷史悠遠,種類繁多。
優點:
①上手易(各種51教程、常式一撈一大把);
②價格低(10塊錢以內各種選擇);
缺點:
①標准51內置功能少,很多功能需外部支持,所以有很多外圍晶元存在(現在國產STC已經彌補了內置功能少的問題,不過工業穩定性不好);
②工業性能不高,應用場景范圍窄(主要應用在教育、消費類產品)
③兼容性低(廠家N多,各行其道,無法統一。除了STC號稱有AT的所有型號兼容品,但同一品牌自身的產品兼容替換就不好)
PIC單片機,不知道怎麼點評。
優點:
①工業穩定性非常好;
②內置功能強大;
③兼容性高(所有一樣封裝的IC都是Pin對Pin);
④應用場景寬(汽車、工業、醫療、安防……而且microchip一直在推陳出新)。
缺點:
①操作繁瑣(即便是老手,也沒法完全摸透PIC的使用套路,新手更難,寫個流水燈都夠你琢磨一段時間了);
②埠特性很講究(同意的程序,這口能實現的,那口不一定能實現);
③硬體堆棧也講究(低級單片機(便宜貨)堆棧只有2~3級,就是主函數調一個子函數,子函數再調一個子函數就到頭了;不過目前常用的都已經是8級、16級堆棧了,夠用了);
④價格貴(動不動就是10塊20塊的)。
⑤對除法的運算能力低下(一條除法要上千個周期);
⑥無越級移位能力(個人總結,所謂越級,是8位單片機竟然不能操作1<<16操作(結果為long型))。
AVR單片機,沒使用過!!
4. c51單片機復位電路的工作原理
如S22復位鍵按下時:RST經1k電阻接VCC,獲得10k電阻上所分得電壓,形成高電平,進入「復位狀態」
當S22復位鍵斷開時:RST經10k電阻接地,電流降為0,電阻上的電壓也將為0,RST降為低電平,開始正常工作
(4)51單片機總結擴展閱讀:
復位電路是一種用來使電路恢復到起始狀態的電路設備,它的操作原理與計算器有著異曲同工之妙,只是啟動原理和手段有所不同。復位電路,就是利用它把電路恢復到起始狀態。就像計算器的清零按鈕的作用一樣,以便回到原始狀態,重新進行計算。
和計算器清零按鈕有所不同的是,復位電路啟動的手段有所不同。一是在給電路通電時馬上進行復位操作;二是在必要時可以由手動操作;三是根據程序或者電路運行的需要自動地進行。復位電路都是比較簡單的大都是只有電阻和電容組合就可以辦到了,再復雜點就有三極體等配合程序來進行了。
單片機復位電路主要有四種類型:
(1)微分型復位電路:
(2)積分型復位電路:
(3)比較器型復位電路:
比較器型復位電路的基本原理。上電復位時,由於組成了一個RC低通網路,所以比較器的正相輸入端的電壓比負相端輸入電壓延遲一定時間.而比較器的負相端網路的時間常數遠遠小於正相端RC網路的時間常數。
因此在正端電壓還沒有超過負端電壓時,比較器輸出低電平,經反相器後產生高電平.復位脈沖的寬度主要取決於正常電壓上升的速度.由於負端電壓放電迴路時間常數較大,因此對電源電壓的波動不敏感.但是容易產生以下二種不利現象:
(1)電源二次開關間隔太短時,復位不可靠:
(2)當電源電壓中有浪涌現象時,可能在浪涌消失後不能產生復位脈沖。
為此,將改進比較器重定電路,如圖9所示.這個改進電路可以消除第一種現象,並減少第二種現象的產生.為了徹底消除這二種現象,可以利用數字邏輯的方法和比較器配合,設計的比較器重定電路。此電路稍加改進即可作為上電復位和看門狗復位電路共同復位的電路,大大提高了復位的可靠性。
5. 總結mcs51單片機p0 p1 p2 p3的功能和特性
mcs51單片機的特性:
P0.0~P0.7 P0口8位雙向口線(在引腳的39~32號端子)。
P1.0~P1.7 P1口8位雙向口線(在引腳的1~8號端子)。
P2.0~P2.7 P2口8位雙向口線(在引腳的21~28號端子)。
P3.0~P3.7 P3口8位雙向口線(在引腳的10~17號端子)。
P0口有三個功能:
1、外部擴展存儲器時,當做數據匯流排(如圖1中的D0~D7為數據匯流排介面)。
2、外部擴展存儲器時,當作地址匯流排(如圖1中的A0~A7為地址匯流排介面)。
3、不擴展時,可做一般的I/O使用,但內部無上拉電阻,作為輸入或輸出時應在外部接上拉電阻。
總結如下:
單片機(Single-Chip Microcomputer)是一種集成電路晶元,是採用超大規模集成電路技術把具有數據處理能力的中央處理器CPU、隨機存儲器RAM、只讀存儲器ROM、多種I/O口和中斷系統。
定時器/計數器等功能(可能還包括顯示驅動電路、脈寬調制電路、模擬多路轉換器、A/D轉換器等電路)集成到一塊矽片上構成的一個小而完善的微型計算機系統。
在工業控制領域廣泛應用。從上世紀80年代,由當時的4位、8位單片機,發展到現在的300M的高速單片機。
6. 如何學習c51單片機
1,樓主,你首先要學習C語言基礎,就相當於80%會單片機了,因為現在所有8/16/32位(51系列,MSP430系列,ARM系列)都是使用C語言。
2,先看內核8051的單片機:台灣宏晶的STC89C51-DIP40/或其它如新茂,到網上買一個開發板,價格不會超過200元。
3,看一下單片機功能:包換內部FLASH、RAM、TIMER、INT、ADC、USB、ISP/IAR等。
4,編譯環境、編程軟體KEIL。
5,打開開發板的例子程序,在KEIL編譯,下載到板,看結果和說明是不是相符,達到這樣效果時,你心裡肯定很激動,這時你真正學會了單片機,你成功了!!!!!。
6,單片機C語言舉例:
sbitSPEAK=P1^5; //IO口定義為蜂鳴器控制
如果蜂鳴器正極接電源,SPEAK=0;蜂鳴器就會響,你看就是這么簡單。
unsiggnechartemp;//定義TEMP為單位元組變數。
sfrPORTAP1;//將P1口定義為PORTA
總結:多做開發板上的實驗,你要多參與這樣的項目開發,慢慢就會了。
7. 51單片機指令長度,有兩位元組三位元組,指令寄存器長度是一個位元組,指令長度等於指令寄存器長度不矛盾嗎
指令長度等於指令寄存器長度,這並不矛盾。對於51單片機,執行指令時,是先取第一個位元組的指令碼,也叫操作碼,進行解碼,然後執行。如果是單位元組指令就執行操作了。如果是兩個位元組,或三個位元組的指令,就會再取出第二位元組,第三個位元組,第二,第三位元組通常是立即數,地址,偏移量等操作數,取出這些操作數後,就會進行計算,或按立即數,地址傳送數據了,是偏移量就計算目標地址然後跳轉了。
總結,51單片機是逐一位元組取出指令碼的,並不是一下子全部取出二個,三個位元組的指令。而只有第一個位元組才是操作碼,其餘是操作數。