一.基本概念
1.裸机程序 BSP
BSP:bord suppord pack 板级支持包
就是程序编写的内容是没有操作系统的,直接通过代码去控制寄存器,让硬件按照要求去工作。
主要内容:51单片机 IMAX6ULL
2.linux驱动部分
在裸机BSP程序的基础上,把对硬件管理的方法,算法,流程,挪到linux系统中,让linux能识别,操作硬件,相当于程序建立在linux系统上。
主要内容:系统移植 驱动
3.CPU、MPU、GPU、MCU
1. CPU(Central Processing Unit)中央处理器
功能: 电脑和大多数电子设备的“核心大脑”,负责执行程序指令、进行运算和控制数据流。
常见于: 电脑、手机、平板等。
特点: 通用性强,适合复杂多任务处理。
2. GPU(Graphics Processing Unit)图形处理器
功能: 专门用于图形、图像、视频的渲染和并行处理,也被广泛用于AI计算。
常见于: 游戏显卡、AI训练服务器、手机、图形工作站。
特点: 并行处理能力强,适合执行大量相同操作(如矩阵计算)。
3. MPU(Microprocessor Unit)微处理器单元
功能: 一种更小、更简单的 CPU,常用于嵌入式系统中(如洗衣机、微波炉)。
常见于: 工业控制、小型智能设备。
特点:
没有内置内存(RAM、ROM)或IO接口,需要外接。
更灵活但需要更多外围电路。
4. MCU(Microcontroller Unit)微控制器单元
功能: 一体化的嵌入式系统,集成了 CPU(或MPU)、内存、I/O接口等。
常见于: 家电、汽车、智能硬件、IoT设备。
特点:
集成度高(“一颗芯片搞定一切”)
功耗低、成本低、适合简单控制任务
简单对比总结表:
缩写
全称
用途
是否集成RAM/ROM
应用举例
CPU
Central Processing Unit
通用计算
否
PC、笔记本、服务器
GPU
Graphics Processing Unit
图像处理、并行计算
否
显卡、AI服务器
MPU
Microprocessor Unit
嵌入式计算(需要外设)
否
工控板、定制电子系统
MCU
Microcontroller Unit
嵌入式控制(单芯片系统)
是
空调、遥控器、智能灯泡
如果你想了解这些在某个具体应用(如AI、嵌入式、机器人)中的角色,我可以进一步解释。
4.寄存器
在数字电路中,用来存放二进制数据或代码的电路称为寄存器。
外设寄存器有固定的地址。
5.RAM和ROM
ROM(只读存储器)和 RAM(随机存取存储器)是计算机中两种常见的内存类型,它们在功能、特性和用途方面有显著区别。以下是详细的概念与对比:
一、ROM(Read-Only Memory)只读存储器
1. 概念:
ROM 是只能读取、不能随意写入的内存。
数据在断电后仍然保留,属于非易失性存储器。
通常由厂家在生产时写入数据,主要用于保存固件(Firmware),如 BIOS(基本输入输出系统)。
2. 特点:
数据不可更改或只能有限更改(如 EEPROM 可多次擦写,但速度慢)。
通常存储启动程序、硬件初始化信息等关键内容。
启动时首先读取 ROM 中的内容来引导系统。
二、RAM(Random Access Memory)随机存取存储器
1. 概念:
RAM 是可读写的内存,数据可以随时读写。
数据在断电后会丢失,属于易失性存储器。
是 CPU 执行程序时的临时工作区域,用来存储正在运行的程序和数据。
2. 特点:
访问速度快,是系统运行的关键。
包括两类:
DRAM(动态 RAM):常用作主内存,成本低,需不断刷新。
SRAM(静态 RAM):速度更快,用于缓存,如 CPU 的缓存(Cache)。
三、ROM 与 RAM 的主要区别对比表
项目
ROM
RAM
全称
Read-Only Memory
Random Access Memory
读写性
通常只读
可读可写
易失性
非易失性(断电不丢失)
易失性(断电数据丢失)
用途
存储固件、启动程序(如 BIOS)
存储临时数据和程序运行信息
是否可修改
通常不可修改(或需特定方法修改)
可随时读写
速度
较慢
较快
例子
BIOS、固件、游戏卡带中的程序
操作系统加载后使用的内存、缓存等
四、简单类比
你可以把它们比作:
ROM:像一本印刷好的说明书,只能看不能写(或要特别工具才能改),断电不会丢失。
RAM:像一块写字板,你可以随便写写擦擦,但一断电内容就没了。
如需我进一步解释 EEPROM、Flash ROM 或是 RAM 的扩展类型如 DDR4/DDR5 等,也可以继续问我!
二.环境搭建
1.keil安装
2.STC-ISP
下载使用。
三.工程
1.LED
1.原理图
从原理图得知是P2寄存器,控制8个led灯,高电平关,低电平开。
2.位运算
&= :是让指定位置0;
P2 &= ~(1 << 0):就是让bit0位为0,打开。
|=:是让指定位置1;P2 |= 1 << 0 :就是让bit0位为1,关闭。
^=:是让指定位反转;P2 ^= 1<< 0:就是让bit0位反转
3.代码
led.c
#include "led.h"
#include <reg52.h>//初始化
void init_led(void)
{P2 = 0xff;
}
//打开某个灯
void led_on(unsigned char n) //1111 0000
{P2 = ~n;
}
//打开所有灯
void led_all_on()
{P2 = 0x00;
}
//关闭某个灯
void led_off(unsigned char n)
{P2 = n;
}
//关闭所有灯
void led_all_off()
{P2 = 0xff;
}
//反转某个灯
void led_nor(unsigned char n)
{P2 ^= n;
}
//反转所有灯
void led_all_nor()
{P2 ^= 0xff;
}
main.c
#include <reg52.h> //register
#include "led.h"
//延时
void Delay(unsigned int xms)
{while (xms--) //每次需要10us{}
}
int main(void)
{unsigned char i = 1;int n = 0;init_led();//全部反转
// while(1)
// {
// led_all_nor();
// Delay(0x3fff);
// }//来回跑流水灯while(1){for(n = 0;n < 7;++n){led_on(i);Delay(0x3fff);i <<= 1;if(n == 6){for(n = 0;n < 7;++n){led_on(i);Delay(0x3fff);i >>= 1; }}}}
}
led.h
#ifndef _LED_H_
#define _LED_H_extern void init_led(void);
extern void led_on(unsigned char n);
extern void led_all_on();
extern void led_off(unsigned char n);
extern void led_all_off();
extern void led_nor(unsigned char n);
extern void led_all_nor();#endif
2.digister
1.原理图
有四个显示,分别由P10,P11,P12,P13控制,当为高电平的时候,打开相应的数码管。
abcdefg dp由P0寄存器控制,当为1的时候,相应的位置亮起来。
2.代码
digister.c
#include <reg52.h>
#include "digiter.h"void bit_select(int n)
{P1 &= ~(0x00ff << 0); //全部置0P1 |= (1 << n); //bit n位置1
}void seg_select(int n)
{ //定义一个显示0~9数字对应寄存器状态的数组code unsigned char segs[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};P0 = segs[n];
}void display(int n)
{int t = 0;while(n != 0){int m = n % 10;P0 = 0;bit_select(t++);seg_select(m);Delay(300);n /= 10;}}
code是把这个数组变量放在ROM区,不是放在RAM区。
main.c
#include <reg52.h>
#include "digiter.h"
void Delay(unsigned int num)
{while(num--){}
}int main(void)
{int i = 0;while(1){//for(i = 0;i < 10;++i)//{// bit_select(0);// seg_select(i);// Delay(0xffff);//}display(i++);if(i > 9999){i = 0;}}}
digister.h
#ifndef _DIGITER_H_
#define _DIGITER_H_extern void bit_select(int n);
extern void seg_select(int n);#endif
3.key
1. 原理图
这是输入,当按键按下去之后,拉低P1,从而通过判断P1的状态而做相应的处理。
2.代码
void init_key(void)
{P1 |= (0x0f << 4);
}int key_pressed(void)
{int ret = 0;if((P1 & (1 << 4)) == 0){ret = 1;}else if((P1 & (1 << 5)) == 0){ret = 2;}else if((P1 & (1 << 6)) == 0){ret = 3;}else if((P1 & (1 << 7)) == 0){ret = 4;}return ret;
}
4.中断
1.概念
当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。
2.五个中断源
51单片机有五个中断源:外部中断0(INTO)定时器0中断、外部中断1(INT1)、定时器1中断、串口(UART)中断、定时器2中断。对应的中断号。
3.中断流程
1.中断源发出中断请求
2.CPU检查是否响应中断及该中断源是否被屏蔽;
3.检测中断优先级;
4.保护现场;
5.执行中断服务函数;
6.恢复现场
4.中断向量
中断向量表:是一个函数指针数组,数组里面保存中断服务函数的入口地址。51单片机的中断向量有五种:外部中断0/1,timer0/1,串口。
5.中断和轮询区别
中断(Interrupt)和轮询(Polling)是两种常见的CPU与外设通信方式,它们在处理外设请求、数据传输等方面有明显区别:
✅ 一、定义
方式
定义
轮询(Polling)
CPU定期主动查询外设是否需要服务,一直轮询外设状态。
中断(Interrupt)
外设在需要服务时主动向CPU发出中断请求,CPU暂停当前工作来响应外设。
✅ 二、工作原理
🔁 轮询
CPU执行主程序的同时,定期检查设备状态。
如果设备准备好了,就处理它的请求;否则继续主程序。
不停检查,浪费资源。
⚡中断
外设完成某个操作或需要处理时,向CPU发送中断信号。
CPU暂停当前任务,保存现场,跳转到中断服务程序(ISR)处理外设请求,处理完再恢复原任务。
✅ 三、对比总结
项目
轮询(Polling)
中断(Interrupt)
主动方
CPU
外设
CPU效率
低(忙等待)
高(只在需要时才响应)
实时性
差,响应不及时
好,可快速响应事件
实现复杂度
简单
复杂(需中断机制、ISR等)
适用场景
少量设备、实时性要求不高的场景
设备多、实时性要求高的场景
资源占用
高,占用CPU时间
低,CPU可做其他任务
✅ 举个例子
假设你在等一个包裹:
轮询:你每5分钟就下楼查看快递到了没(浪费时间、效率低)。
中断:快递员来了按门铃,你再下楼取(只在必要时行动,效率高)。
如果你需要进一步了解硬件电路实现或在单片机(如STM32、8051)中如何设置中断和轮询,我也可以继续说明。
6.代码
1.外部中断
int g_i;
void eint0_handler(void) interrupt 0
{++g_i;
}
void eint1_handler(void) interrupt 2
{--g_i;
}void init_einto(void)
{P3 |= (1 << 2) | (1 << 3);IE |= (1 << 7) | (1 << 0) |( 1<< 2);TCON |= (1 << 0) | (1 << 2);}
2.定时器中断
#include <reg52.h>
#include "timer.h"
#include "led.h"#define HZ200 63231unsigned g_n = 0;
void timer_handler(void) interrupt 1
{
// TH0 = 64613 >> 8;
// TL0 = 64613;
// ++g_n;
// if(g_n >= 100)
// {
// led_all_nor();
// g_n = 0;
// }TH0 = HZ200 >> 8;TL0 = HZ200;P2 ^= (1 << 1);}void init_timer(void)
{TMOD &= ~(0x0f << 0);TMOD |= (1 << 0);
// TH0 = 64613 >> 8;
// TL0 = 64613;TH0 = HZ200 >> 8;TL0 = HZ200;IE |= (1 << 7) | (1 << 1);TCON |= (1 << 4);}
TH0 = HZ200 >> 8;TL0 = HZ200;P2 ^= (1 << 1);
这里高将counter的值的高八位保存在TH0中,所以将counter向右移动8位,那么低八位原来的位置就变成高八位,一个TH0占8位,刚好存放高八位。
低八位直接赋值,高八位丢失,那么TL0刚好存放低八位。
5.定时器
工作原理:配置TMOD确认工作模式和计数来源,再向TH0/TL0写入计数初值,工作开始后计数溢出时,触发中断服务函数。
1.定时器计算
以16位的定时器举例,如何计算定时器
16位 = 2^16 - 1 = 65535,定义计数的值位unsigned short 类型,当超过65535的时候,counter0就会变成0。
晶振的频率为12MHz,12分频之后,cpu 的工作频率就是1MHz。
假如要一个1ms刷新的值,那么 1MHz的情况下, 1 / 1M秒,counter0加1,也就是1us加1,要想1ms刷新,那么就需要conunter就要加1000次,也就是将counter的初始值设定为65535-1000 = 64534,这样1000次之后coeunter变成0。实现1ms。
65535 - 0.001 /(1 / 1000000) = 65535 - 1000 = 64535.
2.PWM
1.概念
PWM(脉宽调制,Pulse Width Modulation)是一种通过改变**脉冲信号高电平持续时间(即“脉宽”)**来控制模拟量输出的技术。它常用于电机控制、LED调光、音频合成等应用中。
✅ 一、PWM是什么?
PWM 是一种数字方式模拟连续模拟信号的方法:
高电平表示“开”,低电平表示“关”
通过控制高电平持续的比例(占空比)来调节输出的平均电压
虽然是开关信号,但其平均电压对外表现为模拟值
✅ 二、PWM的波形特征
PWM波形是一种周期性方波,每一个周期内:
有一段时间是高电平(ON)
剩余时间是低电平(OFF)
✅ 三、PWM的关键指标
指标名称 含义说明 频率(Frequency) PWM信号的周期性重复速率,单位是 Hz。频率 = 1 / 周期时间。 周期(Period) 一次完整高+低电平持续的总时间。周期 = 高电平时间 + 低电平时间。 占空比(Duty Cycle) 高电平持续时间占一个周期的百分比。占空比 = 高电平时间 / 总周期时间 × 100% 分辨率(Resolution) PWM占空比的可调精度,常用位数表示,如8位(256级)、10位(1024级)等。 幅度(Amplitude) 高电平电压的大小,例如3.3V、5V等。通常由电源电压决定。
✅ 四、PWM平均电压计算
PWM 实际输出的 平均电压 可以表示为:
Vavg=占空比×V高电平V_{\text{avg}} = 占空比 \times V_{\text{高电平}}例如:5V PWM,占空比为 60% ⇒ 平均电压 = 5V × 0.6 = 3V
✅ 五、PWM应用举例
应用场景 PWM作用 LED调光 改变LED亮度(占空比高 → 更亮) 电机调速 控制电机平均电压,调节转速 音频输出 生成模拟音频信号 伺服控制 控制舵机角度(特定脉宽映射角度)
如果你在使用具体平台(如STM32、Arduino、ESP32)开发PWM应用,我也可以帮你讲解代码和硬件细节。是否需要这方面的帮助?
2. 200Hz,50%占空比的PWM怎么计算
200hz 就是 1 / 200 = 0.005秒刷新一次,PWM占空比是50%,也就是高低电平的时间各占一半,那么高电平的保持时间为0.0025,
65535 - 0.0025 / (1 / 1000000) = 65535 - 2500 = 63235.
2.代码
编写程序实现按下不同的按键使蜂鸣器产生不同的音调
6.蜂鸣器
1.原理图
2.代码
temer.c
#include <reg52.h>
#include "timer.h"
#include "key.h"unsigned int hz = 100;void timer_handler(void) interrupt 1
{TH0 = 65535 - (11059200 / (24 * hz)) >> 8;TL0 = 65535 - (11059200 / (24 * hz));P2 ^= (1 << 1);
}void init_timer(void)
{TMOD &= ~(0x0f << 0);TMOD |= (1 << 0);TH0 = 65535 - (11059200 / (24 * hz)) >> 8;TL0 = 65535 - (11059200 / (24 * hz));IE |= (1 << 7) | (1 << 1);TCON |= (1 << 4);}
main.c
#include <reg52.h>
#include "timer.h"
#include "key.h"
#include "digiter.h"
#include "delay.h"
int main(void)
{init_timer();init_key();while(1){int key = key_pressed();if(key == 1){hz = 100;}else if(key == 2){hz = 200;}else if(key == 3){hz = 400;}else if(key == 4){hz = 600;}display(hz);}}
3.占空比
通过调节一次中断后的计数器,也就是高电平变成低电平产生中断之后,计数器时间变化,同理,低电平变成高电平之后也进行一次计数器的设置,这样循环下去就可以。
7.uart
1.串口通信
嵌入式系统中的通信是指两个或两个以上的主机之间的数据互交,这里的主机可以是计算机也可以是嵌入式主机,甚至可以是芯片。主机间通信的方式一般可以分为两类:并行通信和串行通信。并行通信是指多个比特同时通过并行线进行传输,这种方式的传输速率较高,但会占用大量的芯片资源;串行通信是指将数据拆分成一个个比特,按照先后次序在一根总线上进行发送,串行通信有着系统占用资源少,结构简单等优点,是主机间通信的常用方式。串口通信(Serial Port)是串行通信的一种,属于串行通信中的异步通信。我们经常听到的RS232、RS485、RS422都是串行通信。
- 单工模式(Simplex Communication):主机间通信时如果一方固定为发送端另外一方固定为接收端,通过一根总线实现数据通信。这种通信方式就像是你只能听别人说话,但无法回答他们一样,只能单向传递信息。
- 半双工通信(Half-Duplex Communication)是一种通信方式,其中数据传输可以在两个方向之间交替进行,但不能同时进行。换句话说,通信双方可以既发送数据又接收数据,但不能同时进行这两种操作。比方说,就像你可以和别人交替说话和倾听对方说话一样。当你在说话时,对方在听你说;当对方在说话时,你在倾听对方。这种方式允许双方之间在发送和接收数据之间切换,但不能同时进行。半双工通信常用于对话式交流和一些简单的通信场景中。
- 全双工通信(Full-Duplex Communication)是一种通信方式,其中数据传输可以同时在两个方向进行,允许通信双方同时发送和接收数据,实现双向通信。就像打电话一样,你可以同时说话也可以听对方说话,双方可以同时进行数据传输,实现双向沟通。
- 作为常用的串行通信方式,以TTL为例,串口通信在不同主机之间的数据格式为:
- 空闲时数据线为高电平;
- 发送发发送一个低电平表示起始位;
- 发送的第一个比特是最低为(最右边);
- 校验位分为奇校验,偶校验和无校验。奇校验是指确保数据位加上校验位中"1",1的总数为奇数;偶校验是指确保数据位加上校验位中"1",1的总数为偶数;
- 为保证下一个字节发送前的起始位能够表现出来,校验位之后发送一个停止位1。
- 数据的传输速率问题:
很明显上图的纵坐标为电压值,横坐标就是时间了。无论起始位、数据为还是停止位、校验位,每个比特在数据线上的时间决定了数据传输的速率。串行通信用波特率(bit per second)来描述数据传输的速率,记作bps。常见的波特率有1200、2400,4800,9600,115200等,表示每秒钟传输的比特数。以9600为例,表明每秒能传输9600个比特。每个比特传输时所需的时间为1/9600秒=1.041*10-4秒。
- 同步通信和异步通信
串口通信时,收发双方的波特率必须是事先约定好的,否则数据传输就会出现混乱。很明显,为保证每个比特占用数据线的时间,发送方和接收方需按照各自的系统计时且双方之间的误差不能太大。通常不能超过(6%)。这种双方各自“计时”的方式称为异步。就好比在打字一样,每次敲击键盘发送一个字母,速度快慢由打字者自己控制,没规定每敲击一个字母之间要等多久,只要保证接收端能够正确识别并解析即可。同步通信设备之间除了有数据线以为还有一条时钟线(SDA和SCL)。其中SCL就是时钟线(serial clock)。发送方负责控制时钟线的变化,每发送一个比特,都需要将时钟线按照规则进行改变。就好比在合唱团里,大家一起唱歌的节奏是由指挥员指挥的,每个人都按照指挥员的节奏唱歌,保证大家唱的是同一首歌且节奏一致。这种通信方式就称为同步通信。譬如IIC、SPI等。
- 主机间通信时的电器物理问题
主机间通信无论采用并行还是串行方式,都无法避免一个物理现象:导线内阻不为零造成的电压衰减。以之前讨论的TTL电平为例,主机之间的距离会造成高电平在接收端出现衰减现象和串扰(指不同信号之间相互干扰导致信号失真)影响。
- TTL(Transistor-Transistor Logic)通常指的就是芯片引脚产生的电压,这个电压值跟选择的芯片有关,在51单片机系统下是5v;在2440下是3.3v等等。5vTTL通信距离通常被限制在10~20米之间,如果需要更远的距离,怎么解决呢?
- 为解决这个问题IEEE(Institute of Electrical and Electronics Engineers)颁布了RS232标准,其中规定了:
逻辑高电平(逻辑1):在-3V到-15V之间
逻辑低电平(逻辑0):在+3V到+15V之间
收发主机间有三根线,分别是收、发和地,因此RS232是全双工的。
理论上RS232能够传输20~30米。
- 同理RS485使用两根信号线(A和B)来传输数据,通过比较A和B之间的电压差来识别信息,电压范围分别为+7V到+12V和-7V到-12V。正电压表示高电平,负电压表示低电平。这种差分信号传输方式提高了抗干扰能力。RS485的传输距离可达1200米,适用于大范围的数据传输需求。由于采用的是压差,RS485在传输数据的某一时刻,两根线都要用到,所以它是半双工的。
1.uart:异步全双工串行通信。
usart:异步/同步全双工
单工,半双工,全双工。
异步,同步
并行:通信的二者之间有俩根和俩根之上。
串行:只有一根通信线,一个bit一个bite的传输(usb)
TTL: