STC系列51單片機
簡介
都是8051衍生的8位單片機, STC單片機有89/90/10/11/12/15這幾個大系列, 每個系列的特點如下
- 89系列是傳統的8051單片機, 燒錄方法有區別, 但是功能上可以和AT89系列兼容, 屬於12T單片機
- 90系列是89系列的改進型, 12T單片機
- 10和11系列是1T單片機, 有PWM, 4態IO接口, EEPROM等功能, 但都沒有ADC
- 12系列是增強型功能的1T單片機, 型號后綴
AD的帶ADC, 后綴S2的除了ADC還帶雙串口 - 15系列是最新的產品, 內部集成了高精度R/C時鍾, 不需要外部晶振
STC89C52參數
STC89C52和STC89C51的區別僅在於Flash大小, C51為4K而C52為8K. 都有多種封裝, 常見的是體積較大的DIP40寬體雙列
- Flash: 8K bytes
- RAM: 512 bytes
- 內置4KB EEPROM
- 32-bit I/O
- 看門狗定時器, MAX810復位電路
- 3個16-bit定時器
- 4個外部中斷, 一個7向量4級中斷結構和全雙工串行口
- 最高運作頻率35MHz, 6T/12T可選
- VCC是5V, 不要高於這個電壓.
還有規格更高的STC89C516DR, 這個有62K的flash和1280byte的RAM. 如果需要ADC, 可以選 STC12C5A60S2 系列, 1T型的指令時鍾速度比普通STC89系列(12T)快, 做定時時要注意.
晶振
- 內部不帶振盪源, 必須外接晶振
- 采用11.0592MHz,或22.1184MHz,可方便得到串口通訊的標准時鍾.
- 如果使用12MHz, 對應定時器為12分頻, 一個機器周期為1 微秒,便於做精確定時.
普通應用一般都選11.0592MHz晶振, 做毫秒和秒的定時也能做到很精確.
硬件准備
- C51最小開發板
- 一個USB2TTL的轉接卡
- 用於查看輸出的LED+1K限流電阻
Windows下的開發
軟件部分
需要准備用於燒錄的STC-ISP軟件, 以及用於編寫編譯代碼的Keil C51軟件
- 從 https://www.stcmcudata.com/ 下載STC-ISP軟件的簡化版, 當前版本是V6.88F
- STC-ISP的使用說明 http://www.stcmcudata.com/STC8F-datasheet/STC-TOOL-20200821.pdf
- 從 https://www.keil.com/files/uc51/c51v959.EXE 下載Keil C51 V9.59.
STC-ISP安裝
用簡化版(tiny結尾的那個), 這個版本不會跟你要管理權限. 解壓即可不需安裝, 界面非常緊湊. STC這個公司的網站和軟件界面都很有特點, 深受低分辨率屏幕影響的設計方式, 界面上除了字就是字. 濃濃的鄉鎮企業風.
Keil C51安裝
安裝時, 默認安裝到與Keil MDK相同的目錄C:\Keil_V5, 如果之前已經安裝了Keil MDK, 可以使用同一目錄, 與Keil MDK是可以共存的. 安裝完之后在license里可以看到有C51的license記錄
安裝STC設備庫
根據STC-ISP的使用說明, 將STC庫文件安裝到Keil C51環境中.
- 關閉Keil MDK軟件
- 打開STC-ISP
- 點擊右側標簽面板中的
Keil ICE Setting, 點擊Add MCU type to Keil, Add STC ICE driver to Keil這個按鈕 - 然后定位到
C:\Keil_v5目錄, 完成安裝
檢查
- 查看
C:\Keil_v5\UV4\目錄下是否有stc.cdb文件, 判斷是否添加完成 - 查看
C:\Keil_v5\C51\INC\目錄下是否有STC目錄, 這個目錄下是STC芯片的頭文件 - 重啟Keil MDK后, 在
File->Device Database中選擇STC MCU Database可以看到STC下的單片機型號列表
燒錄
接線
| USB2TTL | STC89C52 |
|---|---|
| VCC | P40 VCC |
| GND | P20 GND |
| TX | P10 RxD |
| RX | P11 TxD |
燒錄
STC單片機使用的是ISP(In System Program)燒錄方式, 其原理是在單片機內部固化一段ISP代碼, 上電時檢測是否有連續的d字符, 如果檢測到則進入ISP准備階段, 如果超時沒有收到則執行用戶代碼區. 若進入ISP准備階段, 根據STC定義的協議接收數據幀, 最后完成程序的擦除、寫入. 在ISP准備階段若未收到數據幀, 則超時退出ISP, 執行用戶代碼區.
STC-ISP的使用說明
- 運行STC-ISP
- 選擇燒錄芯片對應的型號
- STC89C52RC/LE52RC, 如果不是RC, 則選擇 STC89C52
- 如果型號選擇不正確, 在Check MCU那一步會一直卡在那里
- 選擇USB2TTL對應的COM口
- 點擊Open Code File 加載燒錄程序
- 設置燒錄選項
- 點擊Download/Program按鈕
如果之前已經連線, 此時STC-ISP的日志窗口會提示Checking target MCU ... , 按Reset開關不能進入燒錄, 需要重新上電才會開始燒錄. 顯示信息如下
Checking target MCU ...
MCU type: STC89C52RC/LE52RC
F/W version: 3.2C
Current H/W Option:
. Current clock frequency: 11.018MHz
. System use 12T mode
. Oscillator gain is HIGH
. Any reset source can stop WatchDog if WatchDog timer is running
. Internal XRAM is ENABLE . ALE pin behaves as ALE function pin
. Do not detect the level of P1.0 and P1.1 next download
. Do not erase user EEPROM area at next download
MCU type: STC89C52RC/LE52RC
F/W version: 3.2C
Re-handshaking ... Successful [0.625"]
Current Baudrate: 115200
Erasing MCU flash ... OK ! [0.188"]
Programming user code ... OK ! [0.953"]
Programming OPTIONS ... OK ! [0.031"]
H/W Option upgrade to:
. Current clock frequency: 11.018MHz
. System use 12T mode
. Oscillator gain is HIGH
. Any reset source can stop WatchDog if WatchDog timer is running
. Internal XRAM is ENABLE . ALE pin behaves as ALE function pin
. Do not detect the level of P1.0 and P1.1 next download
. Do not erase user EEPROM area at next download
MCU type: STC89C52RC/LE52RC
F/W version: 3.2C
Complete !(2021-06-30 21:54:26)
- 如果F/W version是3.2C, 會彈出對話框, 提示這是一個翻新的芯片, 可以忽略. 4.2C的不會彈出此提示.
- STC12C5A60S2 系列在燒錄時會顯示MCU ID, STC89C系列不會
Keil C51代碼結構
與STM32項目比, C51的項目的結構簡單多了. 只需要添加一個reg52.h的頭文件, 剩下的就是自己隨意組合. 這個頭文件也只是命名了一些常量.
創建項目的步驟比較簡單:
- 運行Keil MDK
- 新建項目, 找一個目錄將項目文件保存
- 選擇設備
- 對於STC89C52, 定位到STC89C52RC Series
- 對於STC89C516RD+, 定位到STC89C58RD+ Series
- 對於STC12C5A56S2, 定位到STC12C5A60S2 Series
- 這些都可以使用reg52.h作為頭文件
- 中途會提示你是否要保存startup.a51, 選擇是
- 新建C文件, 寫入代碼
- 編譯
示例代碼
#include <reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit led0 = P0^0;
sbit led1 = P0^1;
sbit led2 = P0^2;
sbit led3 = P0^3;
void delay(u16 i) {
while(i--);
}
void main() {
while(1) {
led0 = 0;
led1 = 0;
led2 = 0;
led3 = 0;
delay(500000);
led0 = 1;
led1 = 1;
led2 = 1;
led3 = 1;
delay(500000);
}
}
對於STC89C52RC, 可以改成#include <STC89C5xRC.H>, 對於STC12C5A60S2系列, 可以改成#include <STC12C5A60S2.H>
這個代碼在 STC12C5AxxS2上運行, LED閃爍速度比在STC89C52RC上運行快很多.
常見問題
SBIT 單Bit, Singl Bit
sbit這個類型定義了SFR中的一個bit, 使用操作符^, 這里是容易引起疑惑的一個地方, 這個符號僅僅在代碼變量聲明的地方可用, 代表了SFR中這個bit的位置, 在程序當中, 這個符號才是標准的bit異或操作符(XOR Operator). The character ^ is used to denote the bit position in the byte address of the SFR. This syntax is only valid for Declaration code lines. If used inside the program, then the ^ operator is the standard bitwise xor operator from the standard C language (not specific to C51).
//方式1和2: 前面的sfr-name/sfr-address是其基礎地址, 一定是要可以被8整除的, 例如0xD0, 0xA8, 后面的bit-position取值可以是0~7
sbit name = sfr-name ^ bit-position;
sbit name = sfr-address ^ bit-position;
//方式3: 必須是0x80-0xFF之間的一個值, 例如0xD2, 0xD7, 0xAF
sbit name = sbit-address;
其中
- name 變量名
- sfr-name 已經定義的SFR變量
- bit-position 在SFR變量中的位置
- sfr-address SFR的地址
- sbit-address 此bit的地址
在8051應用中經常需要直接訪問SFR中某一個位的信息, 這種情況就可以使用sbit. 例如:
sbit EA = 0xAF;
上面代碼定義了一個sbit類型的變量EA, 其地址在0xAF. 在8051中這是中斷開啟寄存器中的enable all位的地址.
注意: 使用sbit訪問的對象存儲通常認為是little endian (LSB first), 如果使用sbit訪問標准類型的數據時要留意.
注意:
- 不是所有SFR都可以按位尋址, 只有地址能被8整除的SFR才能, 即低位必須是0或8. 計算SFR的按位地址, 0xC8^6 = 0xC8 + 6 = 0xCE.
- sbit只能在函數外定義.
SFR - 特殊功能寄存器(Special Function Register)
在keil.com上有說明 https://www.keil.com/support/man/docs/c51/c51_le_sfrs.htm
sfr常用於定義寄存器地址, 對應一個8 bit volatile的值. 用法為
sfr name = address;
其中
- name SFR的名稱
- address SFR的地址
SFR變量的定義和其他C變量一樣, 區別僅在於不使用char或者int, 例如
sfr P0 = 0x80; /* Port-0, address 80h */
sfr P1 = 0x90; /* Port-1, address 90h */
sfr P2 = 0xA0; /* Port-2, address 0A0h */
sfr P3 = 0xB0; /* Port-3, address 0B0h */
P0, P1, P2 和 P3 都是SFR的聲明和定義, 等號后面的值必須是8位數值, 8051支持的SFR地址通常為0x80-0xFF. 而NXP 80C51MX提供額外的SFR地址區間0x180-0x1FF. 注意, SFR變量不能定義在函數中, 而必須定義在主代碼中, SFR變量永遠是volatile的, 編譯器不會去優化這種類型的變量的訪問.
sfr16 占用兩個內存單元, 值域為 0~65535. sfr16 和 sfr 一樣用於操作特殊功能寄存器, 不一樣的是它用於操作占兩個字節的寄存器, 例如定時器 T0 和 T1.
C51寄存器的介紹 http://www.51hei.com/mcuteach/245.html
Flash和EEPROM的區別
EEPROM也叫 E2PROM, 稱為電可擦可編程只讀存儲器, 和EEPROM類似, 寫上去的東西也能擦掉重寫, 但它要方便一些, 不需要光照, 只要用電就能擦除或者重新改寫數據, 方便許多, 而且壽命也很長(幾萬到幾十萬次).
FLASH 閃存, 屬於EEPROM的改進產品, 最大特點是必須按塊(Block)擦除(每個區塊的大小不同廠家的產品有不同的規格), 而EEPROM則可以一次只擦除一個字節(Byte). FLASH現在常用於大容量存儲, 例如U盤.
C51的變量
c51編譯器中int 和 short 相同,float 和 double 相同, 具體定義
| 類型 | 寬度 | 取值范圍 |
|---|---|---|
| unsigned char | 1字節 | 0~255 |
| signed char | 1字節 | -128~+127 |
| unsigned int | 2字節 | 0~65535 |
| signed int | 2字節 | -32768~+32767 |
| unsigned long | 4字節 | 0~4294967295 |
| signed long | 4字節 | -2147483648~+2147483647 |
| float | 4字節 | -1.175494E-38~+3.402823E+38 |
| 指針 | 1-3字節 | 對象的地址 |
| bit | 位 | 0 或 1 |
| sfr | 1字節 | 0~255 |
| sfr16 | 2字節 | 0~65535 |
| sbit | 位 | 0 或 1 |
C51中定義一個變量的格式如下
[存儲種類] 數據類型 [存儲器類型] 變量名
- 在定義格式中除了
數據類型和變量名是必要的, 其它都是可選項 - 存儲種類有四種: 自動(auto), 外部(extern), 靜態(static)和寄存器(register). 缺省類型為auto.
- 數據類型即變量類型
- 存儲器類型指定該變量在單片機硬件系統中所使用的存儲區域, 並在編譯時准確的定位. 注意在AT89c51芯片中RAM只有低128位, 80H-FFH的高128位在52芯片中才有用, 並和特殊寄存器地址重疊.
把最常用的命令如循環計數器和隊列索引放在內部數據區能顯著的提高系統性能, 變量的存儲種類與存儲器類型是完全無關的.
C52的RAM分塊和尋址
STC89C52 共有 512 字節的 RAM, 定義的變量都是直接存在 RAM 里邊的. 但是這 512 字節的 RAM是分塊的, 塊與塊之間在物理結構和用法上有區別.
RAM 分為片內 RAM和片外 RAM
- 片內 RAM
標准 51 的片內 RAM 地址從 0x00H~0x7F 共 128 個字節,而現在用的 51 系列都帶擴展片內 RAM,即 RAM 從 0x00 - 0xFF 共 256 個字節. - 片外 RAM
最大可以擴展到 0x0000~0xFFFF 共 64K 字節. - 片內 RAM 和片外 RAM 的地址不是連續的,片內從 0x00 開始, 片外也是從 0x0000 開始.
- 片內和片外的區分來自於早期的 51 單片機,分別指在芯片內部和芯片外部,但現在幾乎所有的 51 單片機(包括STC89C52)芯片內部都集成了片外 RAM, 很少用到真正的芯片外擴展
以下是幾個 Keil C51 中的關鍵字, 代表了 RAM 不同區域
- data: 片內RAM直接尋址區, 從00 - 7F
定義一個變量 a, 可以這樣unsigned char data a=0, 在 Keil 默認設置下,data是默認的可以省略. data 區域 RAM 的訪問在匯編語言中用的是直接尋址, 執行速度是最快的. - bdata: 片內RAM位尋址區, 從20 - 2F這塊地址的16個字節共128個可尋址位, 位地址從00 - 7F
例如
unsingned char bdata sta; // 8位的數據sta
sbit RX_DR = sta^6; //把 8位中的第6位定義為RX_DR, 下同
sbit TX_DS = sta^5;
sbit MAX_RT = sta^4;
- idata: 片內RAM間接尋址區, 從 00-FF
data 是 idata 的一部分. 定義成 idata,不僅僅可以訪問 data 區域,還可以訪問 80-FF 的范圍,訪問idata的時候用的是通用寄存器間接尋址, 速度較 data會慢一些, 平時大多數情況下不太希望訪問 80-FF, 這塊通常用於中斷與函數調用的堆棧, 所以在絕大多數情況下, 使用內部 RAM 只用 data 就可以了. - pdata:片外 RAM 從 00-FF
使用 pdata 定義的變量存到了外部 RAM 的 00-FF 的地址范圍, 這塊地址的訪問和 idata 類似, 都是用通用寄存器間接尋址 - xdata:片外 RAM 從 0000 - FFFF
pdata 是 xdata 的一部分. 定義成 xdata, 可以訪問的范圍更廣泛, 從 0 到 64K 的地址都可以訪問到, 但是它需要使用 2 個字節寄存器DPTRH 和 DPTRL 來進行間接尋址,速度最慢, STC12C5A60S2系列的頭文件中就用到了這個部分
STC89C52的尋址
STC89C52共有 512 字節的 RAM, 分為 256 字節的片內 RAM 和 256 字節的片外RAM. 一般情況下使用 data 區域, data 不夠用了就用 xdata, 如果希望程序執行效率盡量高一點,就使用 pdata 關鍵字來定義.
復位后的各寄存器初始
復位后CPU狀態
PC: 0000H TMOD: 00H
Acc: 00H TCON: 00H
B: 00H TH0: 00H
PSW: 00H TL0: 00H
SP: 07H TH1: 00H
DPTR:0000H TL1: 00H
P0-P3:FFH SCON: 00H
IP: ×××00000B SBUF: 不定
IE: 0××00000B PCON: 0×××0000B
注意P0 - P3是高位
startup.a51文件的作用
80C51在電源重置(Power On Reset)后執行的第一個程序模塊並不是主程序main(), 而是一個KEIL-C51標准鏈接庫中的startup.a51程序模塊.
startup.a51的主要工作, 是把包含idata, xdata, pdata在內的內存區塊清除為0, 並且初始化遞歸指針. 在startup.a51執行完成后, 接着被執行的仍然是一個KEIL-C51標准鏈接庫中的init.a51程序模塊. init.a51的主要作用, 是初始化具有非零初始值設定的變量.
在完成上述的初始化之后, 80C51才會開始執行main()程序.
參考
- 不錯的教程 http://www.51hei.com/mcuteach/252.html
- C51 匯編寫的延時函數說明及時鍾頻率 http://www.51hei.com/mcuteach/247.html
- Very helpful SDCC C51 code examples https://github.com/hungtcs-lab/8051-examples
