单片机
一:基础概念
一:单片机最小系统
单片机:电源+时钟(晶振)+复位 //实现的最小组件
电源:5V直流
时钟(晶振):决定系统运行的速率 一般12M(不超过50M),因为过快导致稳定性,抗干扰性能下降,
分为分频和倍频,一般都是分频(1/(2*6)分频)
复位:电平信号(高/低) 2种:先上升沿,在下降沿; 先下降沿,后上升沿
原理图:表示器件的逻辑连接关系
PCB:表示器件物理连接关系
二:RAM/ROM 芯片自带
RAM :随机存储器 (访问速度快) 4kb //掉电数据丢失
ROM :只读存储器 (访问速度慢) 100~200kb //掉电数据不丢失
三:发光二极管
单个二极管要求电压:0.5v~0.7v 一般给1v~2v 电流:10mv~MAX
四:流水灯
#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 main(void)
{
unsigned char dat = 0;
while(1)
{
P2 = 255 - dat++; //共阳极
delay_ms(500);
};
}
五:显示数字
#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); //22 23 24 控制
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 long num)
{
unsigned char digits[8];
int i = 0;
for (i = 7; i >= 0; i--) {
digits[i] = num % 10;
num /= 10;
}
while(1)
{
for(i=0;i<8;i++)
{
if(digits[i] != 0)
{
digit_select(7 - i);
P0 = array[digits[i]];
delay_ms(2);
}
}
}
}
void main(void)
{
digit_show_num(65536); //51单片机是8位系统,所以要long int
}
六:74HC595(串转并)模块,点亮8*8LED点阵
| 信号名(简写) | 全称 | 功能说明 | 对应引脚 |
|---|---|---|---|
| SER | Serial Data | 串行数据输入(Data) | 14 |
| SRCLK / SPCLK | Shift Register Clock | 移位寄存器时钟(移位控制) | 11 |
| RCLK | Register Clock (Latch Clock) | 锁存时钟(控制输出) | 12 |
| SRCLR | Shift Register Clear | 清除寄存器 | 10 |
| OE | Output Enable | 输出使能,低电平有效 | 13 |
#include <reg51.h>
#include <intrins.h>
sbit SER = P3^4;
sbit SHCP = P3^6;
sbit STCP = P3^5;
sbit BEEP = P2^5;
void delay_us(unsigned char us)
{
while(us--)
{
_nop_(); _nop_(); _nop_(); _nop_(); // 4×NOP指令,大概1us
}
}
void shift_out(unsigned char dat)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
SHCP = 0;
SER = (dat & 0x80) ? 1 : 0;
dat <<= 1;
delay_us(1); //如果移的太快,可能导致芯片识别不到上升沿
SHCP = 1;
delay_us(1); //如果移的太快,可能导致芯片识别不到上升沿
}
//最后再产生一个上升沿
STCP = 0;
delay_us(1);
STCP = 1;
}
void main(void)
{
while(1)
{
BEEP = 0;
shift_out(0xff);
P0 = 0x00;
delay_us(100);
P0 = 0xFF;
}
}
#include <reg51.h>
#include <intrins.h>
sbit SER = P3^4;
sbit SHCP = P3^6;
sbit STCP = P3^5;
sbit BEEP = P2^5;
void delay_us(unsigned char us)
{
while(us--)
{
_nop_(); _nop_(); _nop_(); _nop_(); // 4×NOP指令,大概1us
}
}
void shift_out(unsigned char dat)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
SHCP = 0;
SER = (dat & 0x80) ? 1 : 0;
dat <<= 1;
delay_us(1); //如果移的太快,可能导致芯片识别不到上升沿
SHCP = 1;
delay_us(1); //如果移的太快,可能导致芯片识别不到上升沿
}
//最后再产生一个上升沿
STCP = 0;
delay_us(1);
STCP = 1;
}
unsigned char xin_shape[8] = {
0x99,
0x00,
0x00,
0x00,
0x81,
0xC3,
0xE7,
0xFF
};
void main(void)
{
int i = 0;
while(1)
{
for(i = 0;i<8;i++)
{
shift_out ((1 << i) ); // 列数据(高电平表示点亮)
P0 = xin_shape[7-i];
delay_us(100);
P0 = 0xFF; //消影
}
BEEP = 0;
}
}
#include <reg51.h>
#include <intrins.h>
sbit SER = P3^4;
sbit SHCP = P3^6;
sbit STCP = P3^5;
sbit BEEP = P2^5;
void delay_ms(unsigned int num)
{
unsigned char i,j;
while(num--)
{
i = 2;//看具体晶振大小
j = 199;
}
do
{
while(--j);
}while(--i);
}
void delay_us(unsigned char us)
{
while(us--)
{
_nop_(); _nop_(); _nop_(); _nop_(); // 4×NOP指令,大概1us
}
}
void shift_out(unsigned char dat)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
SHCP = 0;
SER = (dat & 0x80) ? 1 : 0;
dat <<= 1;
delay_us(1); //如果移的太快,可能导致芯片识别不到上升沿
SHCP = 1;
delay_us(1); //如果移的太快,可能导致芯片识别不到上升沿
}
//最后再产生一个上升沿
STCP = 0;
delay_us(1);
STCP = 1;
}
unsigned char num_font_0_9[10 * 8] = {
0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, // 0
0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7E, 0x7E, // 1
0x7E, 0x42, 0x42, 0x06, 0x1C, 0x30, 0x60, 0x7E, // 2
0x7E, 0x42, 0x42, 0x1C, 0x1C, 0x42, 0x42, 0x7E, // 3
0x0C, 0x1C, 0x2C, 0x4C, 0x7E, 0x7E, 0x0C, 0x0C, // 4
0x7E, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x46, 0x7E, // 5
0x3C, 0x60, 0x40, 0x7C, 0x66, 0x42, 0x66, 0x3C, // 6
0x7E, 0x42, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, // 7
0x3C, 0x66, 0x66, 0x3C, 0x66, 0x42, 0x66, 0x3C, // 8
0x3C, 0x66, 0x66, 0x3E, 0x02, 0x06, 0x0C, 0x38 // 9
};
void main(void)
{
unsigned char i = 0;
unsigned char j = 0;
unsigned char k = 0;
unsigned char num = 0;
while(1)
{
if(j % 8 == 0)
num = 80;
else
num = 20;
for(k = 0; k < num; k++)
{
for(i = 0; i < 8; i++)
{
shift_out(1 << (7 - i)); //// 0x80 0x40 0x20 0x10 0x08 0x04 0x02 0x01
P0 = ~num_font_0_9[i + j]; //i + j 表示当前这一帧要显示的列数据
delay_us(100);
P0 = 0xff;
}
}
j++;
if(j > 72)
j = 0;
}
}
七:按键
#include <reg51.h>
void main(void)
{
while(1)
{
if(!(P3 & (1 << 3)))
P2 = 0x55; // 0101 0101
else
P2 = 0xFF; // 1111 1111
if(!(P3 & (1 << 2)))
P2 = 0xaa; // 1010 1010
}
}
#include <reg51.h>
sbit BEEP = P2^5;
void delay_ms(unsigned int num)
{
unsigned char i,j;
while(num--)
{
i = 2;//看具体晶振大小
j = 199;
}
do
{
while(--j);
}while(--i);
}
unsigned char code seg_table[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, // 0~7
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 // 8~F
};
void main(void)
{
unsigned char i = 0;
unsigned char j = 0;
unsigned char key = 0;
while(1)
{
BEEP = 1;
for(i = 0;i<4;i++)
{
P1 = ~(P1 & (1 << i));
for(j = 0;j<4;j++)
{
if(!(P1 & (1 <<j+4)))
{
key = 4*(3-j)+(3-i);
P0 = seg_table[key];
delay_ms(500);
while(!(P1 & (1 <<j+4)));
}
}
}
}
}
八:中断
一:外部中断
#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) // 0~7
{
unsigned char num = P2;
num &= ~(0x7 << 2);
num |= (digit << 2);
P2 = num;
}
// 点阵数据(10个数字图案,每个8字节)
unsigned char code h_data[10 * 8] =
{
0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, // 0
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
};
// 段码(未用,可保留)
unsigned char array[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
// 74HC595 引脚
sbit RCLK = P3^5;
sbit SRCLK = P3^6;
sbit SER = P3^4;
// 简单延时
void delayn(unsigned char n)
{
while(n--);
}
// 发送1字节数据到74HC595
void write_74hc595(unsigned char dat)
{
unsigned char i = 0;
for(i = 0; i < 8; i++)
{
SER = (dat & (1 << (7 - i))) ? 1 : 0;
SRCLK = 0;
delayn(1);
SRCLK = 1;
delayn(1);
}
RCLK = 0;
delayn(1);
RCLK = 1;
}
// 外部中断0初始化
void eint0_init(void)
{
IT0 = 1; // 下降沿触发
EX0 = 1; // 开启INT0中断
EA = 1; // 开启总中断
}
// 当前显示的数字
unsigned char num = 0;
// 中断服务函数,显示数字 +1
void eint0_hanlder(void) interrupt 0
{
P2 = 0xf0; // 可用于调试
num++;
if (num > 9)
num = 0;
P2 = ~num; // 显示在 P2(可接LED)
}
// 外部中断1初始化(INT1,P3.3,下降沿触发)
void eint1_init(void) {
IT1 = 1; // 1 = 边沿触发(下降沿)
EX1 = 1; // 允许外部中断1
EA = 1; // 总中断允许(一般只需设置一次)
}
// 外部中断1服务函数(中断号 2)
void eint1_handler(void) interrupt 2 {
P2 = 0x0f; // 可用于调试或 LED 显示
if (num == 0)
num = 9;
else
num--;
P2 = ~num; // 显示新的值
}
// 主函数
void main(void)
{
unsigned char key = 0;
unsigned char k = 0;
unsigned char i = 0;
unsigned char dst = 0;
unsigned char j = 0;
unsigned char abs = 0;
eint0_init(); // 初始化外部中断
eint1_init();
while(1) // 显示当前 num 指向的数字
{
for(j = 0; j <= abs; j++)
{
for(k = 0; k < 30; k++)
{
for(i = 0; i < 8; i++)
{
write_74hc595(1 << (7 - i)); // 列选
P0 = ~h_data[i + num * 8]; // 行码
delayn(100); // 简单延时
P0 = 0xff; // 消影
}
}
}
}
}
二:定时器
TFO:定时器/计数器T0溢出中断标志。T0被允许计数以后,从初值开始加1计数,当最高位产生溢出时,由硬件置“1”TFO,向CPU请求中断,一直保持CPU响应该中断时,才由硬件清“O”TFO(TFO也可由程序查询清“O”)
TRO:定时器T0的运行控制位。该位由软件置位和清零。当GATE(TMOD.3)(第三位)=0,TR0=1时就允许T0开始计数,TR0=0时禁止T0计数。当GATE(TMOD.3)=1,TR1=0且INTO输入高电平时,才允许TO计数。
TRO=0 //停止计数
TRO = 1 //启动计数
GATE = 0 //直接计数
= 1 //INTO为高电平时计数
定时器/计数器工作模式寄存器TMOD:
定时和计数功能由特殊功能寄存器TMOD的控制位C/T进行选择,TMOD寄存器的各位信息如下表所列。可以看出,2个定时/计数器有4种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各
//定时器0
#include <reg51.h>
// 硬件接口定义
sbit RCLK = P3^5;
sbit SRCLK = P3^6;
sbit SER = P3^4;
// 延时函数(简易版)
void delayn(unsigned char n) {
while(n--);
}
// 74HC595 写入一个字节
void write_74hc595(unsigned char dat) {
unsigned char i;
for(i = 0; i < 8; i++) {
SER = (dat & (1 << (7 - i))) ? 1 : 0;
SRCLK = 0;
delayn(1);
SRCLK = 1;
delayn(1);
}
RCLK = 0;
delayn(1);
RCLK = 1;
}
// 数码管图案数据(0~9)
unsigned char code h_data[10 * 8] = {
0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, // 0
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
};
// 全局变量
unsigned char num = 0; // 当前显示的数字
// 外部中断0初始化(下降沿触发)
void eint0_init(void) {
IT0 = 1;
EX0 = 1;
EA = 1;
}
// 外部中断0服务函数
void eint0_handler(void) interrupt 0 {
P2 = 0xf0; // 可用于调试
num++;
if (num > 9) num = 0;
P2 = ~num; // 可接 LED 显示数值
}
// 外部中断1初始化(INT1,P3.3,下降沿触发)
void eint1_init(void) {
IT1 = 1; // 1 = 边沿触发(下降沿)
EX1 = 1; // 允许外部中断1
EA = 1; // 总中断允许(一般只需设置一次)
}
// 外部中断1服务函数(中断号 2)
void eint1_handler(void) interrupt 2 {
P2 = 0x0f; // 可用于调试或 LED 显示
if (num == 0)
num = 9;
else
num--;
P2 = ~num; // 显示新的值
}
void timer0_init(void)
{
TH0 = 0x4C;
TL0 = 0x00; //15535/256
TMOD &= ~(0x1<< 2);
TMOD &= ~(0x1<< 3);
TMOD &= ~(0x3 << 0);
TMOD |= (0x01 << 0);
ET0 = 1; // 允许定时器0中断
EA = 1;
TR0 = 1;
}
void timer0_handler(void) interrupt 1
{
static unsigned char timer0_num = 0;
TH0 = 0x4C;
TL0 = 0x00;
TR0 = 1;
timer0_num++;
if(20 == timer0_num)
{
num++;
if (num == 9)
num = 0;
timer0_num = 0;
}
}
// 主函数
void main(void)
{
unsigned char key = 0;
unsigned char k = 0;
unsigned char i = 0;
//unsigned char num = 0;
unsigned char dst = 0;
unsigned char j = 0;
unsigned char abs = 0;
eint0_init();
timer0_init();
while(1) // 0 4 8
{
for(j = 0; j <= abs; j++)
{
for(k = 0; k < 30; k++)
{
for(i = 0; i < 8; i++)
{
write_74hc595(1 << (7 - i));
P0 = ~h_data[i + num * 8];
delayn(100);
P0 = 0xff;
}
}
}
}
}
三:24小时时钟
#include <reg51.h>
unsigned char hour = 17;
unsigned char min = 59;
unsigned char sec = 0;
unsigned char code digit_table[] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
void timer0_init(void)
{
TH0 = 0x4C;
TL0 = 0x00; //15535/256
TMOD &= ~(0x1<< 2);
TMOD &= ~(0x1<< 3);
TMOD &= ~(0x3 << 0);
TMOD |= (0x01 << 0);
ET0 = 1; // 允许定时器0中断
EA = 1;
TR0 = 1;
}
void timer0_handler(void) interrupt 1
{
static unsigned char timer0_num = 0;
TH0 = 0x4C;
TL0 = 0x00;
if(++timer0_num >= 20) // 20×50ms = 1s
{
timer0_num = 0;
sec++;
if(sec >= 60)
{
sec = 0;
min++;
if(min >= 60)
{
min = 0;
hour++;
if(hour >= 24)
hour = 0;
}
}
}
}
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); //22 23 24 控制
num |= (digit << 2);
P2 = num;
}
void digit_show_num()
{
unsigned char digits[8];
unsigned char i;
digits[0] = hour / 10; // 小时十位
digits[1] = hour % 10; // 小时个位
digits[2] = 10;
digits[3] = min / 10; // 分钟十位
digits[4] = min % 10; // 分钟个位
digits[5] = 10;
digits[6] = sec / 10; // 秒十位
digits[7] = sec % 10; // 秒个位
for(i = 0; i < 8; i++)
{
digit_select(i);
if(digits[i] == 10)
P0 = 0x40;
else
P0 = digit_table[digits[7-i]];
delay_ms(2);
}
}
// 主函数
void main(void)
{
timer0_init();
while(1)
{
digit_show_num();
delay_ms(100);
}
}
九:串口通信
通信:无线和有线
单工 半双工 全双工
并行:多个数据线 串行:一根数据线
同步:通信双方使用同一个时钟,SPI信息帧,有CLK引脚
异步:通信双方使用不同时钟,双方要固定的数据帧(0起始,1停止)和传输速度(波特率,一般都是9600bps = 1s:单位时间传输了多少个码元,这里用二进制码元),无CLK引脚
空闲时总线保持高电平。起始信号:由高到低 起始信号:由低到高
数据位:5,6,7,8位 需要起始位:1~2位
eg:11001100 实际数据:0x33,数据头是低位
一:UART串口
#include <reg51.h>
unsigned char recv = 0;
unsigned char code digit_table[] = {
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
};
void digit_select(unsigned char digit)
{
unsigned char num = P2;
num &= ~(0x7 << 2);
num |= (digit << 2);
P2 = num;
}
void serial_server() interrupt 4
{
if(TI)
TI = 0;
if(RI)
RI = 0;
recv = SBUF;
switch(recv)
{
case '1':digit_select(0); P0 = digit_table[0];break;
case '2':digit_select(1); P0 = digit_table[1];break;
case '3':digit_select(2); P0 = digit_table[2];break;
case '4':digit_select(3); P0 = digit_table[3];break;
case '5':digit_select(4); P0 = digit_table[4];break;
case '6':digit_select(5); P0 = digit_table[5];break;
case '7':digit_select(6); P0 = digit_table[6];break;
case '8':digit_select(7); P0 = digit_table[7];break;
default:P0 = 0xff;
}
}
void main(void)
{
#if 1
SCON = 0x50;
TMOD &= 0x0F;
TMOD |= 0x20;
TL1 = 0xFD;
TH1 = 0xFD;
ES = 1;
EA = 1;
TR1 = 1;
#endif
//P0 = 0xEF;
while(1);
}
972

被折叠的 条评论
为什么被折叠?



