最近准备面试一家公司,公司主要用8位机去做的,所以现在我来先了解一下51,我学习单片机是直接从STM32开始学的,最真实的感受就是太吃力了。
一、硬件基础差异
| 维度 | C51(8051 系列) | STM32(ARM Cortex-M) | 对编程的影响 |
|---|---|---|---|
| 位宽/架构 | 8 位,经典哈佛结构(程序/数据存储器分离) | 32 位,改进哈佛 + 总线矩阵 | C51 运算慢、内存小,必须手动管理存储区;STM32 接近"电脑式"编程 |
| 主频/性能 | 通常 12~24MHz,指令多周期 | 几十~几百 MHz,单周期指令 | C51 适合简单控制,STM32 可跑复杂算法/RTOS |
| 内存 | RAM 几十~几百字节,Flash 几 KB | RAM/Flash 几十 KB~几 MB | C51 变量必须指定存储类型,否则溢出 |
| 外设 | 基础(GPIO、定时器、串口) | 丰富(ADC/DAC、DMA、USB、CAN 等) | C51 直接操作寄存器;STM32 有 HAL 抽象层 |
二、开发环境与编译器差异
-
C51
:只能用 Keil C51(uVision 老版本),严格的"方言 C"。不支持标准 C 的很多特性,必须用扩展关键字。
-
STM32
:Keil MDK / STM32CubeIDE / IAR / GCC 都行,使用标准 C/C++ ,配合 STM32CubeMX + HAL/LL 库,代码更规范、可移植性强。
C51 存储类型修饰符:
-
data:片内直接寻址 RAM(最快,128 字节)
-
idata:片内间接寻址 RAM(全 256 字节)
-
xdata:片外数据存储器(最常用外部扩展)
-
code:程序存储器(ROM/Flash,只读,常用于常量表)
-
bdata:可位寻址区(20H~2FH)
unsigned char data flag; // 片内快速变量unsigned int xdata buffer[100]; // 外部 RAM 数组unsigned char code table[] = {1,2,3}; // 放在 Flash
四、特殊关键字对比
C51 有 19 个扩展关键字,STM32 几乎全用标准 C。
| 关键字 | C51 作用 | STM32 对应方式 | 示例(C51) |
|---|---|---|---|
| sfr | 定义特殊功能寄存器(SFR) | 直接用寄存器结构体或 HAL | sfr P1 = 0x90; |
| sbit | 定义 SFR 的某一位(最常用!) | HAL_GPIO 或 BSRR 寄存器 | sbit LED = P1^7; |
| bit | 定义普通 1 位变量 | bool / uint8_t | bit flag; |
| interrupt | 定义中断服务函数 | NVIC + 函数名固定 | void isr() interrupt 1 |
| using | 指定寄存器组(0~3) | 无 | using 1 |
| reentrant | 可重入函数(支持递归/中断调用) | 标准 C 支持 | void func() reentrant |
| at | 指定变量绝对地址 | attribute ((at())) 或 linker | unsigned char xdata var at 0x8000; |
| small/large/compact | 内存模型(影响指针大小和代码效率) | 无 | small(默认) |
// C51 点灯(超级简洁)sbit LED = P1^7;LED = 0; // 亮灯(低电平有效)// STM32 点灯(HAL 风格)HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
STM32 HAL → C51 完整重写
1. 点灯实验
#include <reg52.h>sbit LED = P1^0; // STM32: LED_Pin = GPIO_PIN_0void main(void){ LED = 1; // STM32: HAL_GPIO_WritePin(..., GPIO_PIN_SET); while(1) { LED = 0; // 亮灯(低电平有效) // 简单延时(实际项目用定时器代替) unsigned int i = 50000; while(i--); LED = 1; // 灭灯 i = 50000; while(i--); }}
2. 按键实验(检测按键)
#include <reg52.h>sbit LED = P1^0;sbit KEY = P3^7; // STM32: KEY_Pin = GPIO_PIN_7 (低电平按下)void Delay_ms(unsigned int ms) // 简单软件延时(面试可改成定时器){ unsigned int i, j; for(i = ms; i > 0; i--) for(j = 120; j > 0; j--);}void main(void){ LED = 1; // 初始灭灯 while(1) { if(KEY == 0) // STM32: if(HAL_GPIO_ReadPin(...) == GPIO_PIN_RESET) { Delay_ms(20); // 消抖(STM32常用HAL_Delay) if(KEY == 0) // 再次确认 { LED = ~LED; // 翻转LED(STM32: HAL_GPIO_TogglePin) while(KEY == 0); // 松手检测(防止连击) } } }}
3. 定时器实验
#include <reg52.h>sbit LED = P1^0; // STM32: LED_Pinunsigned char count = 0; // 软件计数(实现500ms)// ================== STM32 HAL_TIM 等价中断服务函数 ==================void timer0_isr() interrupt 1 // interrupt 1 = 定时器0{ TH0 = 0x3C; // 重新装50ms初值(STM32: __HAL_TIM_SET_COUNTER) TL0 = 0xB0; count++; if(count >= 10) // 10次 × 50ms = 500ms { count = 0; LED = ~LED; // 翻转LED(STM32: HAL_GPIO_TogglePin) }}void main(void){ // ================== STM32 MX_TIMx_Init 等价初始化 ================== TMOD = 0x01; // 定时器0,方式1 TH0 = 0x3C; // 50ms初值 TL0 = 0xB0; TR0 = 1; // 启动定时器(HAL_TIM_Base_Start) ET0 = 1; // 允许定时器0中断 EA = 1; // 开总中断(__enable_irq()) LED = 1; // 初始灭灯 while(1) { // 主循环可以空着,也可以放其他非阻塞任务(STM32 while(1)风格) }}
感觉要寄了,