catalog
51单片机
串口
自带单片机UART: universal asynchronous receiver transmitter 通用异步接收器
, 来实现 (串口通信)
通过(串口通信), 可实现: (单片机和单片机), (单片机和电脑), 通信(单片机及各模块)
串口的标准接口为: DB9 (9针, 上4下5), 现在计算机没有这样的接口了。 VGAvideo graphics array 视频图形阵列
的接口是: DB15 (15针, 上、中、下各5个) 与图像视频有关 是这个; 比如, 主机与投影仪连接, 连接主机和显示屏, 是VGA接口; 而串口DB9, 仅用于传输数据
简单的双向串口通信, 有2条通信线 (TXD, RXD), 1个VCC, 1个GND TXD: transmit exchange data 发送; RXD: receive exchange data接收
若为单向通信接口, 一条通信线可以
串口0: VCC_0, GND_0, TXD_0, RXD_0 串口1: VCC_1, GND_1, TXD_1, RXD_1 VCC_0 --- VCC_1 GND_0 --- GND_1 TXD_0 --- RXD_1 ' 交叉 ' RXD_0 --- TXD_1
串口常用的 (电平标准):
- TTL电平: 5V为1, 0V为0
- RS232电平: {-3到-15V} 为1, { 3到 15} 为0
注意, 负值 为 1
- RS485电平:
d 两线电压差
d为{ 2到 6}为1, d为{-2到-6}为0 差分
对于(TTL和RS232), 都是一条线, 看他和GND(恒为0V)
对比电压; 而对于(RS485), 他是两条线, 不需要GND, 看两条线的差分值
(TTL和RS232)电平, 最多是10米; 不能太长; 而(RS485电平) 可传输1000米, 这就是 (差分电平)优势
常见(通信接口):
- UART: (引脚为: TXD, RXD), (通信方式: 全双工, 异步), (点对点通信)
- I2C: (引脚为: SCL, SDA) (通信方式: 半双工, 同步) (可挂载多个设备)
- SPI: (通信方式: 全双工, 同步)
- 1-Wire: (通信方式: 半双工, 异步)
- CAN: (在汽车领域, 许多传感器连在一起, 用一根CAN线把它们都挂起来) (差分信号传输, 所以距离很远)
- USB:
(全双工: 同一时刻, 可以发送 可以接收) (单双工: 同一时刻, 要么发送 要么接收, 二选一; 即在一段时间内, 即可发送 也可接收) (单工: 要么发送 要么接收, 二选一)
同步、异步问题:
,比如, (A发送1 0
, 他对应的模拟信号是: --__
, A的频率是1s) ,如果(B以1/2s接收频率, 他会得到: 1 1 0 0
; ), 所以, 这个频率, 要约定好; 这是(同步异步)的问题 (异步: 双方同意相同的频率) (同步: 打开另一个时钟线, 规定频率, 双方不必考虑频率)
(总线): 连接多个设备, 实现设备间的通信
寄存器
单片机, 通过 配置寄存器, 来控制 连接内线
寄存器是 连接 软硬件中介;
寄存器就是 一个特殊的 RAM存储器; (他可以存储 和 读取 数据), 每个寄存器都连接在一起 (一根导线), 即控制 电路连接
寄存器相当于: 一个机器的 操作按钮
定时器
定时器 是 (单片机) 的 内部资源, 不属于 (由IO口控外设)
原理: (定时器) -> 时钟脉冲 -> (计数器) -> 产生中断 -> (中断系统)
计数器为16位, 即最大是65535
定时器, 每个(时钟脉冲), 计数器就 = 1
晶振是12Mhz
(hz是: 赫兹; 是频率单位; 1hz表示: 1s执行一次操作; x hz表示: 1sx操作; 1Mhz: 1s执行1e6次操作
) 51单片机采用12分频, 12Mhz / 12 = 1Mhz
; 1Mhz即: 1s 执行 1e6次操作; 即每个1e-6 = 1us时间, 执行一次操作
即, 每1us
, 会产生脉冲; 即, 计数器 就会 = 1
此时, 假如我们想: (每隔50ms
, 触发一次中断事件), 50ms / 1us = 50000, 即50000个计数
我们知道, 每1us
, 计数就会 =1
; 计数65535
后, 就会 触发中断 所以, 我们的计数器, 要设置初值为: 65535 - 50000 = 15535
void Timer0_Routine() interrupt 1{
//< interrupt function
static unsigned int count = 0;
++ count;
{
//< 重置计数器
TH0 = 15535 / 256;
TL0 = 15535 % 256;
}
if( count >= 20){
//< 20 * 50ms = 1s
//* execute
count = 0;
}
}
void main(){
Timer0_init();
while( 1){
}
}
static void Timer0_init(){
{
//< 定时器模式
TMOD &= 0xF0;
TMOD |= 0x01;
}
{
//<
TH0 = 15535 / 256;
TL0 = 15535 % 256;
}
{
//< ÅäÖÃ
TF0 = 0;
TR0 = 1;
ET0 = 1;
EA = 1;
PT0 = 0;
}
}
138译码器
输入有3个IO口, 输出有8个LED接口
根据你输入的这3个IO口a b c
, 转换为 10进制a*4 + b*2 + c
, 这个数 即{0 -7}
范围的, 就对应 输出
即, 根据你的abc
输入, 就可以选中 8个LED接口的 一个
好处: 减少IO口, 通过3个IO口, 就可以 选中8个接口中的一个
电平
单片机是: (高电平的 驱动能力弱) (低电平的 驱动能力强)
所以, LED/按钮等 都是采用 (低电平) 点亮的方式; 即正常是1(高电平), 0(低电平)是触发状态
(共阴极方式)的 数码管, 在输入正极
端, 会加 (双向数据缓冲器), (缓冲器: 增大驱动能力)
如果IO口直接 接数码管, 则: 单片机输出的数据, 就直接当做 驱动的数据 此时, IO口 通过(缓冲器) 到 数码管, 则: 通过缓冲器, (缓冲器里有VCC) 吸取能量, 然后再输出到 数码管里; 数码管就比较亮
LED数码管
一个数码管, 由8个LED封装在一起, 组成8
的形状,
一个LED, 图形是<
三角形; 箭头的指向, 是阴极
, 即(从阳极到阴极)
这8个LED, 要么8个箭头都指向一个引脚(共阴极连接方式), 否则, 8个箭头指向8个引脚, 一个引脚连接8个LED (共阳极方式)
一个数码管模块, 有10个引脚; 比如以(共阳极方式)为例, (3 和 8)号引脚 连接8个LED的阳极; 8个LED的阴极 连接 (其他8个引脚)
4位一体的数码管, (4个数码管, 共用同一个模块), 外接12个IO口
以(共阴极)为例: (4个数码管L0{0-7}
L1{0-7}
…) 外接IO口的{0-7}, 同时控制 L{0-3}{0-7}; 即, 一个IO口(比如0号), 他同时控制L{0-3}0, 即同时控制4个LED
每个数码管的阴极, 连接一个IO口; 即L0数码管(8个LED), 同时连接1个IO口
一共是8 + 4个IO口;
可以发现, 我们只能让(1个数码管) 显示数字!!! 不可能让4个数码管显示不同的数据; 因为, 如果让4个数码管展示不同数字, (第1, 外接的4个阴极IO口 必须都是0 这样4个数码管才能工作
) (第2, 具体你要展示什么数, 通过8个IO来控制;) 这样, 所有4个数码管, 展示的数字 都是相同的;
如果要展示:1 2 3 4
, 需要用到动态数码管展示技术 (每次展示1个数码管, 然后快速的去展示第2个数码管, …);
首先讲, 如何让一个数码管展示 (通过8个IO口, 控制要展示的数字) (让该数码管的阴极的IO口 为0
, 其他3个数码管的阴极IO口为1
)
这样做的好处: (1, 需要的IO口, 很少; 否则, 你需要8*4 + 1个IO口
)
也有(8位一体)的, 即需要8 + 8
个IO口
void Show_LED_number( int _id, int _num){
if( _id & 1){
P2_2 = 1;}
else{
P2_2 = 0;}
if( _id & 2){
P2_3 = 1;}
else{
P2_3 = 0;}
if( _id & 4){
P2_4 = 1;}
else{
P2_4 = 0;}
if( 0){
}
else if( _num == 0){
P0 = 0x3F;}
else if( _num == 1){
P0 = 0x06;}
else if( _num == 2){
P0 = 0x5B;}
else if( _num == 3){
P0 = 0x4F;}
else if( _num == 4){
P0 = 0x66;}
else if( _num == 5){
P0 = 0x6D;}
else if( _num == 6){
P0 = 0x7D;}
else if( _num == 7){
P0 = 0x07;}
else if( _num == 8){
P0 = 0x7F;}
else if( _num == 9){
P0 = 0x6F;}
Delay_milliSecond( 1);
P0 = 0;
}
(第一步), 位选, 即选中一个数码管 8位一体, 选中其中的一个
(第二步), 段选, 写数字; …这里有一个(影子问题), 当我们(第二步)执行完了, 此时(第x0个数码管, 展示了x1数字)
, 然后接下来, 马上进行第x2个数码管, 展示x3数字
,即动态刷新; 但是, 当执行完选中第x2个数码管
后, 此时, 还是x1数字
, 所以, 此时会有第x2个数码管, 展示x1数字
,不过这会很快被x3数字
刷新掉; 但 仍然会 留下一下影子; ,所以, 这里要(消除 影子), 即, 在展示完数字后,要置为0 (当然不能太快, 等展示出来数字后, 再置0), 这样, 下一次(位选)后, 全是0, 即没有LED点亮
这种通过单片机, 不停的 刷新, 来展示数码管; 有缺点: 大量的占用CPU时间 有(专门的 驱动数码管的芯片), 我们直接调用接口即可
引脚IO口
单片机刚上电时, 所有IO口 都是 高电平
比如, 按键: 默认是不按下的, 即(高电平);
单片机的(IO口)是: 弱上拉模型 (准双向口), 即(既可以输入, 也可以输出 )
引脚冲突
(LED模块), (数码管模块), (LCD屏模块), 都存在 引脚IO口冲突; 你只能选择使用1个模块
独立按键
P3_0 ----> [button] ----> GND
(由于单片机默认是 高电平), 即, 初始状态(未按下), 是:1 -> [] -> 0
按下后, 会变成: 0 -> [] ->0
轻触开关 不分正负极, 这非常重要, 按钮是两端都是0
, 表示 (按下)
int Get_pressed_button_number(){
if( 0){
}
else if( P3_1 == 0){
return 0;}
else if( P3_0 == 0){
return 1;}
else if( P3_2 == 0){
return 2;}
else if( P3_3 == 0){
return 3;}
return -1;
}
矩阵键盘
为了节省IO口的占用, 采用矩阵键盘4 * 4
的方式; 使用4个行 + 4个列 = 8
个IO口; 采用(按行扫描)的方式来读取状态 否则, 他需要(16 + 11表示: 一个输入/输出IO口
)个 IO口
键盘: x0, x1, x2, ..., x15
4个输入 IO口, 一个IO口 同时连接4个键盘{ (x0,x1,x2,x3}, {x4,x5,x6,x7}, ...}
4个输出 IO口, 一个IO口 同时连接4个键盘
在(独立按键)那, 我们知道, (轻触开关不分 正负极!!!), 当两端都是0
表示 按下; 而且, 那里 是 一端为GND, 即恒为0; 所以, 只需判断另一端即可 但是, 这里不同的是: 这里两端都是 (IO口)!!! 所以, 这里, 你就需要自己去测试!!! 即, (让他的一端) 先给置为0 手动的去赋值IO口
, 然后, 你去检测他的 另一端的IO口, 看是否为0
此时有2种方式:
- 按行扫描; (4个行 即4个IO口, 表示所有按键的一端)
比如, (行对应的 IO口for( row = 0; row < 4; ++row){ 将row对应的IO口, 置为0; 其他row的IO口, 置为1; for( col = 0; col < 4; ++col){ if( col对应的IO口 == 0){ // [row, col] 按下了 } } }
P1 _ {4-7}
, 你需要不停的, 给这4个IO口, 赋值为:[0111 -> 1011 -> 1101 -> 1110] 一直赋值
), 然后检测列 - 按列扫描; 即对列进行循环赋值, 然后检测行
包括我们的屏幕, 也是 采用(扫描节省IO口
)方式; 以1920 * 1028
的分辨率为例, 他的每一个单元 即一个(像素点), 按理说 他需要: 1920 * 1080 + 1(一个输入/输出口)
个IO口, 来控制每个像素点; 这是非常非常多的 实际上, 他也是采用(矩阵扫描)的方式, 1920 + 1080
个IO口; 这个扫描, 是由显卡负责的
Keil
由于stc
是国产的, 在选择cpu类型时, 选择at89c52
在option
的 output
里, 勾选上 create HEX file
烧录软件: 不要使用(STC-ISP), 使用 (普中科技软件)!!!
单片机
在我们的PC里, (CPU, RAM, ROM, I/O这些), 都是单独的芯片, 将这些都集成起来, 安装到一个(主板)上, 就是我们的主板
而单片机MCU: micro control unit
, 是将 (CPU, RAM, ROM, I/O这些), 都继承在一个芯片里
单片机有: (8位的: 51单片机), (32位的: STM32)
(芯片), 由2部分组成: (内核) 和 (外设); 类似于: CPU 和 {内存,...} 的关系
ARM公司, 只设计 (内核), 他不生产(芯片);
芯片厂商ST公司 (STM芯片)
, 都是基于这个ARM的 (内核), 来设计 (外设)
(内核) 和 (外设), 是由2个公司设计的; 他们之间, 通过 (总线) 通信
电脑硬件
总线
cpu 与 外部器件(外部器件又称:芯片)需要进行交互
他们之间,就是通过“总线”(本质是导线)进行连接的
cpu对数据的操作
cpu与外部器件(外部器件又称:芯片)的交互,必须知道以下信息:
1,地址信息(需要知道:内存/硬盘/显卡/网卡的 地址)
2,控制信息(读取/写入 数据)
3,数据信息(数据的内容)
' 这3种信息,对应到物理总线上: 地址总线/控制总线/数据总线 '
(cpu) 通过 <地址/控制/数据 3种总线> 与 (内存) 进行数据通信
' 这里的“内存”,并不单指内存条,还包括:显卡的内存、网卡的内存 '
cpu读数据
cpu 从 “内存:不单指内存条” 读取数据
1, cpu通过 地址总线,发送一个地址Tar
2, cpu通过 控制总线,发送“读指令”
3, cpu通过 数据总线,得到“Tar地址”的数据
cpu写数据
cpu 往 “内存:不单指内存条” 写入数据
1, cpu通过 地址总线,发送一个地址Tar
2, cpu通过 控制总线,发送“写指令”
3, cpu通过 数据总线,发送数据Data(Tar地址的数据,会被更新为Data)
存储器
其实计算机的各个部件,都有“属于他自己“的存储器,比如显卡的显存
存储器是划分为了多个“存储单位”
比如,一个存储器 他有128个存储单元
广义上的“内存”,并不单指内存条,还包括:显卡的内存、网卡的内存
显存
比如显存: 一个显卡是无法单独运行的,显卡里面有个内存,叫做显存
你把数据存到“显存”里面,显卡里面有个叫做gpu的处理器
会把显存里的数据,映射到屏幕上(gpu的速度,要比cpu还快)
磁盘
磁盘是不同与内存的, 磁盘里的数据/程序,必须要放到内存里,才能被cpu使用
cpu -> 内存 -> 磁盘/光盘/硬盘
比如磁盘里的“xx游戏”, 运行时,他需要先 “加载到内存”
这样,cpu才能和内存进行交互数据,这个游戏才能运行起来
虚拟内存
当内存不够时,会增加一个“虚拟内存”
虚拟内存: 将一些不经常用到的内存,临时放到“硬盘”里
需要的时候,再将其从“硬盘” 放到内存里。
寄存器
寄存器: 嵌入到cpu内部的内存,即cpu内部的存储器
cpu内有很多寄存器,比如ax、bx 这些都是寄存器的名称
有的寄存器存储的是:数据
有的寄存器存储的是:指令
单片机
最以前, 他叫做: 单板机 将'cpu,存储器,io设备(小键盘/LED
显示器)'等,装在一块印刷电路板上 后来,演变为 单片机(单芯片微型计算机) 单片机使用的是 TTL电平(+5V为 高电平) 单片机是 从计算机 通过'串口' 把程序下载到 单片机里面 这里的串口, 是使用的RS232串口 (他的高电压是-12V) ' 即,计算机 与 单片机 通信时, 两者的 电平标准不同!! { +5V 和 -12V 无法通信}' ' 故, 需要使用 “电平转换芯片”(max232) 即,将+5V 转换为-> -12V ' 单片机运行起来的 '必要条件': 1,电源 2,晶振(就像人的心跳,需要不停的跳动) 3,复位电路(单片机在上电时,必须进行一次复位) 对单片机任意IO口的 操作: ' MSC51有4个8位IO口,即有32个位 ' 1, 你可以, 输出该位的 电平高低 2, 你可以, 输入IO口的电平
MSC51
MSC51是 Intel公司推出的 一系列单片机的总称,有“8031, 8051 ”等型号
其中,最经典的是 8051型号。 所以,习惯用8051 来代指 MSC51单片机
8位cpu、4K的rom、128的ram、4个8位并口、1个全双工串行口
寻址范围是 64K
总线
bus总线: 计算机各个部件之间传送信息的公共通道
分为: 内部总线 和 外部总线
外部总线,分为: 数据总线Data bus(DB)、地址总线AB、控制总线CB
CPU
由: '运算逻辑 、 控制逻辑 、 中断系统 、 寄存器' 组成
IO口, 引脚
引脚是分8个为一个组, P0、P1、P2、P3组(称为: 四个8bit 并行口)
每组有8个,即P0.0 P0.1 P0.2 P0.3 ... P0.7
这个8,就对应CPU的位数(因为CPU的位数是8!!!)
T/C
timer计时器 和 counter计数器
ROM
rom: read-only mem 只读
只能读出,预先以存储好了的数据
(即,'硬盘'的概念: 数据保存在其中,不会丢失)
(但也不完全是硬盘,因为硬盘是可读可写的)
RAM
ram: random access mem 随机存储
断电丢失数据,用于存储 '短时间内所运行的程序'
(即,'内存'的概念: 负责程序的运行,数据交换)
(在计算机运行的过程中,一些随机的变量 可以实时的调用)
晶振
程序是一条一条命令来执行的。
命令A -> 命令B
比如,现在是在“命令A” 如果没有晶振,就会一直在“命令A”
晶振的作用是: 给单片机提供“时钟”,驱动它,一步一步的往下走
复位
reset复位RST, 复位电路: 让程序 从“最开头”开始执行
电容
作用是“滤波”,电源VCC可能会不稳定,就像是“水流” 时快时慢
电容: 就像一个“池子”,先预先存储一些水(电荷),然后再从池子里 放出去
这样, 放出去的 就比较稳定
符号是 =
, 他的英文capacitor 容量
, 简写cc
LED
开发板会默认带有8个LED,称为LED模块, LED: light emit diode 发光二极管 (正极VCC) -> [限流电阻] -> [L-8个LED-R] -> P2.[0-8]引脚 限流电阻的作用是: VCC的电压是5V,电流比较大; 串联一个电阻后,电流就会变小 此时, LED的L端 已经是“正电”了; 则LED的R端: 接负电,则灯亮; 接正电,灯不会亮 ' 此时,问题转换为: 控制P2.[0-8]引脚 输出 高(+5V) 还是 低(0V) 电平的问题。 ' 要深入这个问题,就需要先理解寄存器的功能 单片机内部:[ cpu -- 寄存器 -- 驱动器 -- 引脚 ] 寄存器,其实就是一种存储器,他里面有8个bit,这个8个bit就对应外部的8个引脚 比如,这个寄存器叫做P2 那么他对应的8个引脚 就是P2.[0-7] 8个bit,通过8个驱动器,连接8个引脚。 驱动器: 增大电流 cpu,通过你写的程序 直接的访问这个寄存器,即直接往寄存器里 写值 比如,往P2寄存器的[0]位,写1 -> 则,他会通过驱动器,最终给P2.0引脚 提供高电平+5V 0号LED 左右都是正, 所以他不会亮。 只有当他是0,即提供低电平,灯才会亮。 ' 即,cpu通过 控制寄存器里的值, 来控制 外部的硬件电路 '
C-51语言
C-51语言,在C语言的基础上,拓展了新的功能:
1, 新添了一些 '基础数据类型'
sfr: 特殊功能寄存器
sbit: 特殊功能位
sfr16: srf的16位数据
bit: 位变量
sbit bit = PSW^2 'PSW是单片机内的一个寄存器,一个寄存器是8位'
' ^2的意思是,该寄存器的第2位; 即,你直接操作bit 就是在操作寄存器 '
for循环
for( int i = 0; i < 10; ++i){}
这是错的!!!
必须是: int i;
for( i = 0; i < 10; ++i){}
数据类型
注意, int 是2字节的+- 32767
!!! int i; for( i = 0; i < 100000; ++i){}
这就死循环了!!!
long是 4字节
函数内static变量
static变量, 必须写在 (函数最开头)!!! 他的前面, 不可以有非static
以下代码是错的; 必须让static int b;
在前面
void Func(){
int a;
static int b;
}