嵌入式开发硬件——单片机
1、单片机最小系统
单片机外部:
电源
时钟:12M晶振,1/(2*6)分频【决定工作效率】
复位:(强制重启)
单片机内部:
RAM(随机存储器):掉电数据丢失,运行在这里。访问速率快
ROM(只读存储器):掉电数据不丢失,文件保存在这里,访问速率慢
2、51相关知识
有电势差(1v-2v),led灯才能亮
原理图:表示器件的逻辑连接关系;PCB:表示器件的物理连接关系
电容:滤波作用
电阻:负载作用,限制电流,排阻(104表示 10的3次方)
3、流水灯
时钟周期:12M hz
机器周期:
1M hz(主频)
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,其他位不变
练习:输入四位数字,显示该四位数字
#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
#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, //数字10x00, 0x3c, 0x08, 0x10, 0x20, 0x3c, 0x00, 0x00,//数字20x00, 0x3c, 0x04, 0x04, 0x3c, 0x04, 0x04, 0x3c, //数字30x00, 0x04, 0x04, 0x04, 0x04, 0x3c, 0x04, 0x04, //数字40x3C, 0x20, 0x20, 0x3C, 0x04, 0x04, 0x3C, 0x00, // 数字50x00, 0x3c, 0x20, 0x20, 0x3c, 0x24, 0x3c, 0x00, //数字60x00, 0x3c, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00, //数字70x7E, 0xC3, 0xC3, 0x7E, 0xC3, 0xC3, 0xC3, 0x7E, //数字80x00, 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;elsenum = 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+jj = 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
代码:
void main()
{while(1){if(!(P3 & (1<<3)))//代表得到P33P2=0X23;elseP2=0Xff;if(!(P3 & (1<<2)))P2=0Xbb;}
}
7、矩阵按键模块
重点,有些许没搞懂
代码:
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数字减小
#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, //数字10x00, 0x3c, 0x08, 0x10, 0x20, 0x3c, 0x00, 0x00,//数字20x00, 0x3c, 0x04, 0x04, 0x3c, 0x04, 0x04, 0x3c, //数字30x00, 0x04, 0x04, 0x04, 0x04, 0x3c, 0x04, 0x04, //数字40x3C, 0x20, 0x20, 0x3C, 0x04, 0x04, 0x3C, 0x00, // 数字50x00, 0x3c, 0x20, 0x20, 0x3c, 0x24, 0x3c, 0x00, //数字60x00, 0x3c, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00, //数字70x7E, 0xC3, 0xC3, 0x7E, 0xC3, 0xC3, 0xC3, 0x7E, //数字80x00, 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
代码:实现时间显示
#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;0x3cTL0=0x00; //(65535-50000)%256;0cafTMOD &=~(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":不动作,总线由上拉电阻维持高电平
上课认真听,不要依赖于视频,不懂知识点记下来,自学给它搞懂
看一遍代码,再独立自己写