1、单片机最小系统
单片机外部:
电源
时钟:12M晶振,1/(2*6)分频【决定工作效率】
复位:(强制重启)
单片机内部:RAM(随机存储器):掉电数据丢失,运行在这里。访问速率快
ROM(只读存储器):掉电数据不丢失,文件保存在这里,访问速率慢
2、51相关知识
有电势差(1v-2v),led灯才能亮
原理图:表示器件的逻辑连接关系;PCB:表示器件的物理连接关系
电容:滤波作用
电阻:负载作用,限制电流,排阻(104表示 10的3次方)
3、流水灯
时钟周期:12M hz
机器周期:
1M hz(主频)

cpp
void Delay_ms(unsigned int num)
{
unsigned char i, j;
while(num--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
4、数码管
array[]里面存储的是0123456789abcdef,表示的数码管的abcdefg横线的1和0
digit_select()控制数码管显示的位置:
1、0x7 0000 0111>>2=0001 1100 1110 0011 代表p2.2 p2.3 p2.4端口为0
2、将digit(0-7)左移2位,存入P2.2-P2,4,其他位不变

练习:输入四位数字,显示该四位数字
cpp
#include<reg51.h>
void Delay_ms(unsigned int num)
{
unsigned char i, j;
while(num--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void digit_select(unsigned char digit)
{
unsigned char num=P2;
num &= ~(0x7<<2);
num |=(digit<<2);
P2=num;
}
unsigned char array[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void digit_show_num(unsigned int num)
{
unsigned char digit[4];
unsigned int i;
digit[3]=num%10;
digit[2]=(num/10)%10;
digit[1]=(num/100)%10;
digit[0]=num/1000;
for(i=0;i<4;i++)
{
digit_select(i);
P0=array[digit[i]];
Delay_ms(5);
}
}
void main(void)
{
unsigned int dat=1234;
while(1)
{
digit_show_num(dat);
};
}
5、8*8点阵模块
74HC595(串转并)模块
特点:8位串行输入,8位串行或并行输出,100MHZ的移位频率,输出寄存器可以直接清除
p35控制移位,p36控制输出
行数控制移位,列数控制哪个灯亮
行数高电平,列数低电平,灯亮
消影
SRCLK=0;SRCLK=1;上升沿,把每一位数据数据寄存器的数据移位RCLK=0;RCLK=1;上升沿,把移位寄存器的数据给数据存储寄存器
SER

练习:滚动显示0-9
cpp
#include <reg51.h>
sbit RCLK = P3^5;//表示取P3端口的第5位,不是异或
sbit SRCLK = P3^6;
sbit SER = P3^4;
unsigned char code h_data[10 * 8] =
{
0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00,
0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7E, 0x7E, //数字1
0x00, 0x3c, 0x08, 0x10, 0x20, 0x3c, 0x00, 0x00,//数字2
0x00, 0x3c, 0x04, 0x04, 0x3c, 0x04, 0x04, 0x3c, //数字3
0x00, 0x04, 0x04, 0x04, 0x04, 0x3c, 0x04, 0x04, //数字4
0x3C, 0x20, 0x20, 0x3C, 0x04, 0x04, 0x3C, 0x00, // 数字5
0x00, 0x3c, 0x20, 0x20, 0x3c, 0x24, 0x3c, 0x00, //数字6
0x00, 0x3c, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00, //数字7
0x7E, 0xC3, 0xC3, 0x7E, 0xC3, 0xC3, 0xC3, 0x7E, //数字8
0x00, 0x18, 0x24, 0x24, 0x1C, 0x04, 0x24, 0x18 //数字9
};
void delayn(unsigned char n)
{
while(n--);
}
void write_74hc595(unsigned char dat)
{
unsigned char i = 0;
for(i = 0; i < 8; i++)
{
if(dat & (1 << (7 - i)))// 检查当前位是否为1
{
SER = 1;
}
else
{
SER = 0;
}
SRCLK = 0;
delayn(1);
SRCLK = 1;
delayn(1);
}
RCLK = 0;
delayn(1);
RCLK = 1;
}
void main(void)
{
unsigned char i = 0;
unsigned char j = 0;
unsigned char k = 0;
unsigned char num = 0;
while(1)
{
//首列(h_data[j])会被显示 80 次,其他列(h_data[j+1]~h_data[j+7])显示 20 次。
if(j % 8 == 0)
num = 80;
else
num = 20;
for(k = 0; k < num; k++)//显示很多遍
{
for(i = 0; i < 8; i++)
{
write_74hc595(1 << (7 - i));
P0 = ~h_data[i + j];//共阳极,低电平有效
delayn(100);
P0 = 0xff; // 消除残影
}
}
j++;
if(j > 72)//上面有i+j
j = 0;
}
}
6、独立按键模块
注意点:
LED模块显示的数字是从右往左看,且亮灯代表0,不亮灯代表1
比如:
0x23(0010 0011),led显示应为:
1100 0100
不亮,不亮,亮,亮,亮,不亮,亮,亮
!(P3 & (1<<3)) 记住1<<3 0000 0001 0000 1000
P3=0XB0 1011 0000
1011 0000 & 0000 1000 一假则假
0000 0000
取反 1111 1111

代码:
cpp
void main()
{
while(1)
{
if(!(P3 & (1<<3)))//代表得到P33
P2=0X23;
else
P2=0Xff;
if(!(P3 & (1<<2)))
P2=0Xbb;
}
}
7、矩阵按键模块
重点,有些许没搞懂

代码:
cpp
unsigned char get_key_value(void) {
unsigned char col, row_val;
// 扫描 4 列(P1.0-P1.3)
for (col = 0; col < 4; col++) {
P1 = ~(1 << col); // 当前列置低电平,其他列高电平
if ((P1 & 0XF0) != 0xF0) { // 如果有按键按下(行不全为1)
// 检测具体哪一行被按下,并控制 P2 输出
if (!(P1 & (1 << 4))) P2 = ~(0x10 >> col); // 第1行
if (!(P1 & (1 << 5))) P2 = ~(0x0C >> col); // 第2行
if (!(P1 & (1 << 6))) P2 = ~(0x08 >> col); // 第3行
if (!(P1 & (1 << 7))) P2 = ~(0x04 >> col); // 第4行
}
}
return 0;
}
void main()
{
unsigned char key=0;
unsigned char dst=0;
while(1)
{
key=get_key_value();
if(key!=0)
{
dst=key;
}
}
}
8、外部中断
程序顺序执行时,产生中断,要去执行中断服务程序,然后再回来执行原程序
51单片机5个中断源的优先级:

详见如图:

代码:实现按k3数字增加,k4数字减小
cpp
#include<reg51.h>
sbit SRCLK=P3^6; //11 SRCLK管脚
sbit RCLK=P3^5; //12 RCLK管脚
sbit SER=P3^4; //14 SER管脚
unsigned char code h_data[10 * 8] =
{
0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00,
0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7E, 0x7E, //数字1
0x00, 0x3c, 0x08, 0x10, 0x20, 0x3c, 0x00, 0x00,//数字2
0x00, 0x3c, 0x04, 0x04, 0x3c, 0x04, 0x04, 0x3c, //数字3
0x00, 0x04, 0x04, 0x04, 0x04, 0x3c, 0x04, 0x04, //数字4
0x3C, 0x20, 0x20, 0x3C, 0x04, 0x04, 0x3C, 0x00, // 数字5
0x00, 0x3c, 0x20, 0x20, 0x3c, 0x24, 0x3c, 0x00, //数字6
0x00, 0x3c, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00, //数字7
0x7E, 0xC3, 0xC3, 0x7E, 0xC3, 0xC3, 0xC3, 0x7E, //数字8
0x00, 0x18, 0x24, 0x24, 0x1C, 0x04, 0x24, 0x18 //数字9
};
void delay(unsigned char i)//延时函数
{
while(i--);
}
void Hc595pro(unsigned char dat) //发送段选数据函数
{
unsigned char a=0;
for(a=0;a<8;a++)
{
if(dat&(1<<(7-a)))
{
SER=1;
}else
{
SER=0;
}
SRCLK=0;
delay(1);
SRCLK=1;
delay(1);
}
RCLK=0;
RCLK=1;
}
void enit0_init(void)
{
IT0=1;
EA=1;
EX0=1;
}
void enit1_init(void)
{
IT1=1;
EA=1;
EX1=1;
}
unsigned char num=0;
void enit0_hanlder(void) interrupt 0
{
P2=0X0f;
if(num<9)num++;
P2=~num;
}
void enit1_hanlder(void) interrupt 2
{
P2=0Xf0;
if(num>0)num--;
P2=~num;
}
void main()
{
unsigned char key = 0;
unsigned char k = 0;
unsigned char i = 0;
unsigned char dst = 0;
unsigned char j = 0;
unsigned char abs = 0;
enit0_init();
enit1_init();
while(1) // 0 4 8
{
for(j = 0; j <= abs; j++)
{
for(k = 0; k < 30; k++)
{
for(i = 0; i < 8; i++)
{
Hc595pro(1 << (7 - i));
P0 = ~h_data[i + num * 8];
delay(100);
P0 = 0xff;
}
}
}
}
}
9、定时器/计数器
计时器/定时器的核心部件是一个加法计数器
1、16位定时器最大计数值65535,所以初值 =65535-50000=15536=0x3caf
2、TMOD &= ~(0x1<<2);清除TMOD的第2位(bit2)
3、TMOD &= ~(0x1<<3);清除TMOD的第3位(bit3)
4、TMOD &= ~(0x3<<0); TMOD |= (0x01<<0);
这两行代码组合起来实现了:
先清零TMOD的bit0和bit1(M1和M0)
然后设置M0=1,M1=0


代码:实现时间显示
cpp
#include <reg51.h>
unsigned char array[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71,0x40};
unsigned char sec = 0;
void delay_ms(unsigned int num)
{
unsigned char i, j;
while(num--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void timer0_init(void)
{
TH0=0x4c; //(65535-50000)/256;0x3c
TL0=0x00; //(65535-50000)%256;0caf
TMOD &=~(0X1<<2);//设置计数器的时钟来源
TMOD &=~(0X1<<3);//GATE设置为0
//设置模式为16位定时器
TMOD &=~(0X3<<0);
TMOD |=(0X01<<0);
ET0=1;
EA=1;
TR0=1;
}
void timer0_hanlder(void) interrupt 1
{
static unsigned char timer0_num= 0;
TH0=0x4c; //(65535-50000)/256;
TL0=0x00; //(65535-50000)%256;
TR0=1;
timer0_num++;
if(20==timer0_num)//20*50ms=1s
{
sec++;
timer0_num=0;
}
}
void digit_select(unsigned char digit,unsigned char seg) // 0~7
{
unsigned char num = P2;
num &= ~(0x7 << 2);
num |= (digit << 2);
P2 = num;
P0=seg;
}
void main(void)
{
unsigned char hour = 23;
unsigned char min = 55;
timer0_init();
while(1)
{
if(60==sec)
{
min++;
sec=0;
if(60==min)
{
hour++;
min=0;
if(24==hour)
{
hour=0;
}
}
}
digit_select(0,array[sec%10]);
delay_ms(2);
digit_select(1,array[sec/10]);
delay_ms(2);
digit_select(2,array[16]);
delay_ms(2);
digit_select(3,array[min%10]);
delay_ms(2);
digit_select(4,array[min/10]);
delay_ms(2);
digit_select(5,array[16]);
delay_ms(2);
digit_select(6,array[hour%10]);
delay_ms(2);
digit_select(7,array[hour/10]);
delay_ms(2);
}
}
10、串口通信
单工,半双工,全双工?
并行:一次性全部传输(两根及以上的数据线)
串行:一个一个传输(一根数据线)
同步:通信双方使用同一个时钟
异步:通信双方使用不同的时钟
波特率:单位时间内传输的码元数(比特率)
小tips:电压是两点之间的电势差,必须两点都有电压
串行口控制寄存器:PCON和SCON


11、DS18B20
DS18B20 采用单总线通信协议,初始化是主机(MCU)与从机(DS18B20)建立通信的第一步。核心步骤如下:
主机发送复位脉冲:拉低总线至少480µs,然后释放(高电平)。
从机响应存在脉冲:DS18B20 检测到上升沿后,等待15-60µs,拉低总线60-240µs作为应答。
总线恢复空闲:从机释放总线,主机检测到应答后确认设备在线。

写0
- 主机拉低总线 60µs~120µs,释放总线,进入恢复时间。
- 从机在主机拉低后的 15µs~30µs 窗口内采样总线
写1:
- 主机拉低总线 1µs~15µs(短暂脉冲)。
- 从机快速释放总线,总线被上拉电阻拉高
读0/1:
- 主机拉低总线 ≥1µs (启动读时间片),在15µs内释放总线 并切换为输入模式(高阻态),在拉低后的 15µs~30µs 窗口内读取总线电平(低="0",高="1")。
- 若从机发送"0" :主动拉低总线并保持至时间片结束(图中
DS1820 active low
),若发送"1":不动作,总线由上拉电阻维持高电平

上课认真听,不要依赖于视频,不懂知识点记下来,自学给它搞懂
看一遍代码,再独立自己写