
簡介本資源為C51單片機設計C語言實例合集配套400個由淺入深的源碼案例覆蓋IO輸出、LED花樣控制、數碼管與按鍵交互、LCD1602/12864液晶顯示、DS1302時鐘、DS18B20測溫、24C02存儲、串口通信、紅外收發、點陣顯示、步進電機及直流電機驅動等常用外設與典型應用適合51單片機初學者入門學習也可作為課程設計、畢業設計及項目開發的參考代碼庫。資源壓縮包約28.52MB內含以.c源文件為主的數百個獨立實例平均每個案例文件精簡可按編號逐例閱讀、燒錄驗證或二次修改循序漸進理解C語言編程思路與寄存器配置方法。資源目前已有3900余人學習下載經過較多學習者驗證涵蓋從點亮第一個LED到綜合項目如密碼鎖、電子鐘、溫度報警等的完整訓練路徑案例分類清晰、目錄直觀便于快速定位所需功能模塊對提升單片機編程與調試能力有較大幫助。1. 從點亮LED到超聲波測距C51單片機設計C語言實例合集的能力圖譜翻一遍這套C51單片機設計C語言實例合集的目錄從“IO輸出點亮1個LED燈方法1”起步到“LED循環左移”“數碼管動態顯示”再到“DS18B20溫度采集”“DS1302時鐘”“1602/12864液晶顯示”最后落到“超聲波測距LCD1286”和“彩屏圖片顯示”。這個排列順序不是隨機的它實際上對應了從GPIO電平控制、片上外設配置到多模塊系統集成的完整進階路徑和大學單片機課程以及藍橋杯等競賽的考察范圍高度重合。所有例程都是可直接編譯的C語言源碼按外設類型編號每個例程都對應一個能觀察到的實驗現象。新手可以用它建立LED、數碼管、按鍵、液晶、傳感器這些基礎模塊的驅動意識做過幾個項目的開發者也能直接抽取其中的I2C、單總線、串口時序代碼快速移植到自己的工程里。下文按顯示控制、片上外設、綜合應用和工程配置幾個層面拆解這套源碼的實際用法。2. GPIO控制與顯示基礎流水燈、數碼管與按鍵掃描的實現思路2.1 IO輸出控制LED的兩種方式與延時函數精度分析合集前兩個實例是點亮LED的兩種方法。第一個用sbit位定義單獨控制引腳第二個對整個端口做字節賦值。兩者在指令層面完全不同位操作鎖定的是可位尋址寄存器字節賦值直接把數據寫入P0寄存器。實際開發中位操作適合控制使能端、片選端這類單引腳信號字節賦值適合一次并行輸出8位數據。// 方法1位尋址方式點亮LED #include reg51.h sbit LED1 P1^0; // 將P1.0位映射為LED1變量 void main(void) { LED1 0; // 共陽接法輸出低電平點亮 while (1) { // 主循環保持LED持續點亮 } }sbit關鍵字把P1.0引腳映射為可操作的位變量后續代碼可讀性更好。方法2用P1 0xFE做整端口賦值0xFE的二進制是1111 1110只有P1.0輸出低電平其余引腳輸出高電平。兩種方式在Keil C51中通用reg51.h頭文件里已經定義好全部特殊功能寄存器和位地址不需要額外配置。與點亮LED配套的是延時函數。合集里大量例程使用軟件延時核心是循環體的指令周期void delay_ms(unsigned int ms) { unsigned int i, j; for (i 0; i ms; i) { for (j 0; j 120; j) { // 空操作消耗指令周期 } } }這個函數在12MHz晶振、Keil默認優化級別下實測每輪約1ms。換成STC11系列1T內核后同一條語句執行速度是傳統8051的12倍內層循環次數必須按比例調小。軟件延時不需要硬件資源但會被中斷程序打擾時序要求嚴格的場合應改用硬件定時器。合集里從“點亮LED”到“定時器使用”的編排順序正好是這條思路的自然延續。2.2 循環移位與查表法實現LED流水燈和花樣燈實例10和11分別實現LED循環左移和循環右移邏輯互為鏡像。左移的核心是組合移位和按位或#include reg51.h void delay_ms(unsigned int ms); void main(void) { unsigned char led 0xFE; // 初始狀態P0.0為低電平點亮 while (1) { P0 led; delay_ms(200); led (led 1) | 0x01; // 左移一位最低位補1 if (led 0xFF) led 0xFE; // 全部熄滅后回到初始位 } }led (led 1) | 0x01中左移一位把當前亮燈位置向高位推| 0x01補上最低位的滅燈電平。判斷led 0xFF是因為所有位都變成1時說明燈已全部熄滅此時重新賦0xFE形成循環。如果換成右移把移位方向反過來0x01換成0x80即可。實例12 “查表顯示LED燈”換了一種思路把運動序列預存到數組unsigned char code led_table[] { 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F }; void main(void) { unsigned char i; while (1) { for (i 0; i 8; i) { P0 led_table[i]; delay_ms(150); } } }查表法的優勢是序列可以任意指定不限于線性位移。實例14 “花樣燈”在表中寫入非線性的亮滅組合實現“兩燈交替、向中間聚攏再分開”這類視覺效果。數組前的code關鍵字把數據放到程序存儲區不占用RAM。C51單片機的內部RAM通常只有128字節對全局數組來說這個關鍵字是必須的。2.3 共陽數碼管段碼表與靜態顯示程序設計從實例16 “共陽數碼管靜態顯示”開始例程引入段碼概念。數碼管的7個段加小數點共8個引腳點亮某段需要在對應引腳輸出電平。共陽接法下0~9的段碼是一個固定查找表// 共陽數碼管段碼表數字0~9 unsigned char code seg_code[] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 };下表列出常用數字在共陽和共陰兩種接法下的段碼值兩者的關系是按位取反顯示字符共陽段碼共陰段碼00xC00x3F10xF90x0620xA40x5B30xB00x4F40x990x6650x920x6D60x820x7D70xF80x0780x800x7F90x900x6F段碼的推導方法共陰接法下按a0x01、b0x02、c0x04、d0x08、e0x10、f0x20、g0x40、dp0x80的位權重累加。顯示數字1時b和c兩個段亮共陰段碼是0x020x040x06共陽取反得0xF9。實例17 “1個共陽數碼管顯示變化數字”在查表基礎上加定時器刷新演示了表驅動加狀態更新的基本結構。2.4 動態掃描與獨立按鍵掃描的實現思路多位顯示時每一位單獨占用一組IO不現實C51的做法是位選加段選兩級結構。實例159 “數碼管顯示鎖存器”和實例160 “數碼管動態顯示”展示了兩種實現前者用鎖存器隔離位選與段選后者直接用P2口位選、P0口段選逐位掃描。unsigned char code bit_sel[] {0xFE, 0xFD, 0xFB, 0xF7}; unsigned char display_buf[4] {1, 2, 3, 4}; void display_scan(void) { unsigned char i; for (i 0; i 4; i) { P2 bit_sel[i]; // 選中第i位數碼管 P0 seg_code[display_buf[i]]; // 輸出對應段碼 delay_ms(2); // 保持2ms } }動態掃描一輪4位的周期是8ms刷新率約125Hz人眼不會察覺閃爍。位與位的切換空隙要盡量短否則顯示有拖影。掃描函數放在定時器中斷里比主循環穩定因為主循環里的其他耗時操作會拉長刷新周期。按鍵掃描經常和數碼管顯示搭配實例19 “按鍵控制單個數碼管顯示”的流程是經典的三段式判斷void key_scan(void) { if (KEY1 0) // 第一次檢測到按下 { delay_ms(10); // 跳過抖動區間 if (KEY1 0) // 再次確認電平穩定 { while (KEY1 0); // 等待按鍵釋放 num; if (num 9) num 0; P0 seg_code[num]; } } }去抖時間取10~20ms是機械按鍵的通用值太短濾不掉抖動毛刺太長影響響應手感。按鍵接在P3口且電路為高電平上拉時檢測到低電平表示按下。STC系列默認的準雙向口帶弱上拉可以直接用這種電路。3. 片上外設與通信接口定時器、串口、I2C與單總線實戰3.1 定時器工作方式選擇與方式1中斷定時實現流水燈和數碼管掃描的延時對精度要求不高軟件延時夠用一旦涉及串口波特率、DS18B20時序、DS1302讀寫就必須用硬件定時器。C51的T0和T1支持四種工作方式選擇依據如下表工作方式計數器位數最大定時12MHz晶振典型應用方式013位約8.192ms兼容早期產品新設計很少用方式116位約65.536ms周期定時、方波發生方式28位自動重裝載約0.256ms串口波特率發生器方式3T0分裂為兩個獨立8位定時器約0.256ms同時需要兩路定時中斷實例167和170分別演示方式1和方式2。方式1的初始化代碼void Timer0_Init(void) { TMOD 0xF0; // 清零T0控制位保留T1原有配置 TMOD | 0x01; // T0設為方式116位定時器 TH0 0x3C; // 初值高8位對應50ms TL0 0xB0; // 初值低8位 ET0 1; // 使能T0中斷 EA 1; // 打開總中斷 TR0 1; // 啟動定時器 }初值計算邏輯12MHz晶振下機器周期1us定時50ms需要計數50000次初值65536-5000015536十六進制是0x3CB0。TMOD 0xF0和TMOD | 0x01的組合是慣用寫法只修改T0相關位不影響T1的配置。中斷服務函數必須重新裝載初值否則定時器從0重新計數周期變成65.536msunsigned char cnt 0; void Timer0_ISR(void) interrupt 1 { TH0 0x3C; // 重裝初值 TL0 0xB0; cnt; if (cnt 20) // 累計20個50ms為1s { cnt 0; P0 ~P0; // 翻轉LED狀態 } }interrupt 1是T0的中斷號Keil C51規定0對應外部中斷0、1對應T0、2對應外部中斷1、3對應T1、4對應串口。中斷函數里不要做耗時操作尤其不要調用軟件延時否則會拖慢主循環并影響其他中斷響應。3.2 串口通信與中斷收發波特率配置與幀處理實例113到115覆蓋串口基本通訊、串口中斷應用和RS485通信。串口方式1是8位異步收發波特率由T1的溢出率決定經典配置void UART_Init(void) { SCON 0x50; // 方式110位一幀REN1允許接收 TMOD 0x0F; // 保留T0配置 TMOD | 0x20; // T1設為方式28位自動重裝載 TH1 0xFD; // 波特率9600 TL1 0xFD; ES 1; // 使能串口中斷 EA 1; TR1 1; // 啟動T1 }TH10xFD對應9600波特率前提是晶振頻率11.0592MHz。這個頻率是專門為串口選的因為它能精確分頻出標準波特率。換成12MHz晶振時誤差約2%短幀通信可用長幀或接Modbus總線時容易出錯。波特率(bps)晶振頻率(MHz)TH1初值誤差960011.05920xFD0%480011.05920xFA0%240011.05920xF40%9600120xE6約2%串口接收用中斷方式避免丟字節數據到達時硬件將SBUF中的數據交給程序并置位RI標志unsigned char rx_buf[16]; unsigned char rx_cnt 0; void UART_ISR(void) interrupt 4 { if (RI) { RI 0; // 清接收標志 rx_buf[rx_cnt] SBUF; // 讀取串口數據 if (rx_cnt 16) rx_cnt 0; // 環形緩沖處理 } if (TI) { TI 0; // 清發送完成標志 } }SBUF是收發共用的特殊功能寄存器讀它得到接收數據寫它發送數據。實際項目中幀接收不能只靠“收滿16字節”判斷還需要幀頭、幀尾和校驗值約束。參考Modbus RTU的幀格式常見做法是收到幀頭后啟動一個定時器兩個字節間隔超過3.5個字符時間就認為幀結束再做CRC校驗。合集里的“串口測溫電腦顯示”實例146就是“接收命令-采集溫度-回傳數據”的完整閉環。3.3 AT24C02的I2C讀寫開機次數與掉電保存的實現實例100 “24c02記憶開機次數”和實例101 “24c02存儲上次使用中狀態”用AT24C02實現掉電保存。AT24C02是2Kb的EEPROMI2C總線訪問兩根線SCL和SDA在空閑時被上拉電阻拉高。void I2C_Start(void) { SDA 1; SCL 1; delay_us(5); SDA 0; // SCL高電平期間SDA出現下降沿起始條件 delay_us(5); SCL 0; } void I2C_Stop(void) { SDA 0; SCL 1; delay_us(5); SDA 1; // SCL高電平期間SDA出現上升沿停止條件 delay_us(5); }起始和停止條件是I2C協議所有事務的邊界。AT24C02的設備地址是0xA0寫和0xA1讀內部有256字節存儲空間。寫周期最長約5ms寫完數據后如果立即讀硬件ACK會一直保持高電平需要軟件輪詢或強制延時。記錄開機次數的核心邏輯count AT24C02_ReadByte(0x00); // 上電讀取保存次數 count; AT24C02_WriteByte(0x00, count); // 寫回地址0這類應用要注意EEPROM的擦寫壽命約100萬次同一地址頻繁寫入會提前磨損工業場景通常加磨損均衡策略比如輪流使用多個存儲地址避免每次都寫同一個位置。3.4 DS18B20單總線時序溫度采集與超溫報警實例122到126覆蓋單路和多個DS18B20的讀取以及超溫報警邏輯。DS18B20使用單總線協議一根數據線既要供電又要傳數據所有時序以微秒級延時為基準操作時序要求說明復位脈沖拉低480us以上再釋放釋放后器件拉低60~240us應答寫0拉低整個60us時隙數據線上保持低電平寫1拉低1~15us后釋放由上拉電阻恢復高電平讀時序拉低1~15us后采樣必須在時隙開始后15us內讀讀取溫度的流程分四步復位、寫命令跳過ROM匹配0xCC、寫轉換命令0x44、等轉換結束然后再次復位、寫讀暫存器命令0xBE最后讀回兩字節溫度數據。unsigned int DS18B20_GetTemperature(void) { unsigned char low, high; unsigned int temp; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳過ROM序列號匹配 DS18B20_WriteByte(0x44); // 啟動溫度轉換 while (DQ 0); // 轉換期間DS18B20拉低總線完成后釋放 DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳過ROM DS18B20_WriteByte(0xBE); // 讀取暫存器 low DS18B20_ReadByte(); // 溫度低字節 high DS18B20_ReadByte(); // 溫度高字節 temp (high 8) | low; // 組合成16位溫度值 return temp; }溫度值格式是高字節的高5位為符號位低11位為數據位。12位分辨率下每LSB對應0.0625℃實際溫度等于原始值乘以0.0625。顯示時先右移4位得到整數部分小數部分單獨處理。實例125 “超溫報警測試”讀取溫度后與閾值比較超過則驅動蜂鳴器這就是“傳感器數據閾值判斷執行機構”的控制系統最小閉環。多個DS18B20掛同一條總線時實例124需要先讀每顆芯片的64位序列號再用0x55命令匹配目標設備否則讀到的數據會混亂。4. 顯示系統與時鐘應用1602/12864液晶、DS1302與綜合項目設計4.1 1602液晶的讀寫時序與字符顯示流程合集多處實例用到1602液晶它的驅動本質是對兩個寄存器的操作寫命令和寫數據。RS引腳區分訪問對象RW區分讀寫方向EN是使能脈沖。void LCD1602_WriteCmd(unsigned char cmd) { RS 0; // 選擇命令寄存器 RW 0; // 寫方向 P0 cmd; // 命令字送到數據口 EN 1; delay_ms(2); // 使能高電平保持時間 EN 0; } void LCD1602_WriteData(unsigned char dat) { RS 1; // 選擇數據寄存器 RW 0; P0 dat; EN 1; delay_ms(2); EN 0; }1602初始化序列一般是延時15ms、寫0x388位總線兩行顯示、寫0x0C開顯示關光標、寫0x06寫入后光標右移、寫0x01清屏。實例175 “液晶1602顯示”的參考工程里有完整的初始化序列。1602內部DDRAM地址第一行從0x00開始第二行從0x40開始定位時用0x80 | 列地址作為命令寫入。寫命令和寫數據之間要留出足夠的執行時間用查忙標志比固定延時更可靠但查忙需要把P0設為輸入模式很多工程師圖省事直接用延時。提示1602模塊如果只有16個引腳而沒有PSB等模式選擇位默認就是8位并口模式。部分模塊帶背光控制引腳BLK接法看模塊絲印確認。4.2 12864液晶的并行/串行驅動與中文字庫顯示實例106、107和176到180覆蓋ST7920主控12864液晶的多種驅動方式。ST7920內置中文字庫顯示中文時寫入GB2312編碼的兩個字節即可不需要自己取模。并行模式下DB0~DB7接P0口RS、RW、EN控制寄存器選通PSB接高電平串行模式PSB接低電平只要CS、SID、SCLK三根線。void LCD12864_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { SCLK 0; if (dat 0x80) SID 1; else SID 0; dat 1; SCLK 1; // 上升沿鎖存數據 } }串行模式在 ST7920 上每一位的傳輸順序是固定的先寫5個連續的1作為同步頭再跟兩個字節的命令和數據。上述循環只演示了單字節移位發送完整驅動里需要按照“同步頭高字節低字節”的幀格式組包。串行模式的優點是省IO缺點是速度慢刷全屏圖形時有明顯的逐行繪制過程。實例131和168在刷圖時先清屏再寫新數據直接在原圖上疊加會產生殘影。4.3 DS1302時鐘芯片驅動與可調日歷實現DS1302是串行時鐘芯片通過CE、SCLK、I/O三線接口訪問。實例102到105從“時鐘原理”到“串口自動更新時間”難度遞增。讀寫DS1302的一個寄存器需要先寫地址字節最低位表示方向unsigned char DS1302_ReadByte(unsigned char addr) { unsigned char i, dat 0; CE 1; // 啟動通信 DS1302_WriteByte(addr | 0x01); // 地址字節最低位置1表示讀 for (i 0; i 8; i) { dat 1; SCLK 0; if (DS1302_IO) dat | 0x80; // 上升沿讀入1位 SCLK 1; } CE 0; // 結束通信 return dat; }DS1302內部時間寄存器全部使用BCD碼存儲讀出來要轉十進制才能顯示寫時間時反過來。實例166 “用DS1302與12864LCD設計的可調式中文電子日歷”把DS1302時間和12864漢字顯示組合按鍵改BCD值后寫回芯片。這里最容易踩的坑是DS1302默認寫保護是開啟的往時間寄存器寫入數據前必須先向地址0x8E寫入0x00關閉寫保護寫完后寫0x80重新打開否則時間寫不進去讀出來一直是初始值。DS1302用外部32.768kHz晶振焊接時晶振兩腳對地電容的容值要匹配通常選6pF不匹配時走時誤差會明顯增大。4.4 綜合項目實例電子鐘、密碼鎖與交通燈的模塊組合合集后半部分大量綜合項目最典型的是“電子鐘類”“密碼鎖類”和“交通燈類”。以實例142 “1602液晶顯示的密碼鎖”為例功能拆分是矩陣鍵盤輸入、EEPROM存儲密碼、1602顯示狀態、繼電器控制開鎖。模塊間通過全局變量交換數據輸入緩存放數組密碼比對用循環逐位bit check_password(void) { unsigned char i; for (i 0; i 4; i) { if (input_buf[i] ! saved_pwd[i]) return 0; // 有一位不匹配即驗證失敗 } return 1; }這種“輸入采集-狀態判斷-輸出控制”的結構是所有綜合項目的主干。實例147 “交通燈測試”把定時器中斷的時基用到極致主循環只做狀態判斷定時器中斷每秒更新倒計時四個方向用狀態枚舉切換。分析這類項目的源碼時建議先畫模塊間的數據流圖再逐模塊對照硬件接線比從頭按行讀代碼高效得多。實例138 “電子琴”則是按鍵掃描加蜂鳴器輸出不同頻率的組合頻率表放在code區按鍵索引直接映射表下標這也是查表法在音頻場景中的典型應用。5. Keil C51工程搭建與代碼復用芯片包安裝、編譯配置與排錯技巧5.1 Keil5添加C51芯片包與工程模板建立Keil MDK5默認只支持ARM內核要編譯C51源碼必須先安裝C51芯片包。安裝完成后新建工程Device欄會出現Atmel、STC等廠家的8051型號。如果列表里沒有STC89C52可以先選Atmel的AT89C52建工程頭文件用reg51.h不影響編譯燒錄時再選STC對應型號。工程必須修改兩個配置Output標簽勾選Create HEX File否則沒有HEX文件可下載Debug標簽按燒錄器選型配置常見的是STC-Link或USB轉串口下載。5.2 常見編譯錯誤與硬件調試排查思路C51編譯報錯最高頻的是C231和C141。C231表示位地址重復定義通常因為兩個sbit變量映射到同一個可位尋址引腳檢查頭文件和源文件的位定義C141表示數組越界或指針訪問非法地址域重點檢查數組下標是否被外部修改。硬件排查按固定順序先電源電壓和復位電路再晶振波形最后下載器連接。程序能下載但現象不對時在main函數第一行翻轉一個IO口用示波器或萬用表確認芯片是否真正運行。使用STC內部IRC時鐘可以省外部晶振但內部時鐘精度約1%左右串口高波特率下還是建議外接11.0592MHz晶振。5.3 基于外設驅動的代碼復用方法把合集里的每個外設驅動拆成“頭文件源文件”的文件對比如ds18b20.h和ds18b20.c頭文件只暴露初始化函數和讀取函數源文件封裝底層時序// ds18b20.h #ifndef DS18B20_H #define DS18B20_H extern bit DS18B20_Reset(void); extern void DS18B20_WriteByte(unsigned char dat); extern unsigned int DS18B20_GetTemperature(void); #endif這樣做的好處是工程從AT89C52換到STC15系列時只需修改源文件里的引腳定義主程序調用接口完全不變。合集的目錄本身就是檢索表做測溫項目查“18B20”相關實例做時鐘項目查“DS1302”和“12864”做通信項目查“串口”和“RS485”幾分鐘就能找到對應工程的驅動文件再按自己的引腳分配改頭文件。合并多個實例時注意兩點reg51.h只包含一次所有sbit引腳定義集中在同一小文件管理。合并前把每個實例原有的main.c改名成功能模塊文件刪掉各自的main函數保留一個總入口調用各模塊初始化函數。這套工作完成后你就擁有了一份屬于自己的C51驅動庫后續做藍橋杯習題、課程設計或實際項目時都可以直接調用而不必每次從頭寫底層時序。本文還有配套的精品資源點擊獲取