串口通信 day48
单片机
一:基础概念
一:单片机最小系统
单片机:电源+时钟(晶振)+复位 //实现的最小组件
电源: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, // 00x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7E, 0x7E, // 10x7E, 0x42, 0x42, 0x06, 0x1C, 0x30, 0x60, 0x7E, // 20x7E, 0x42, 0x42, 0x1C, 0x1C, 0x42, 0x42, 0x7E, // 30x0C, 0x1C, 0x2C, 0x4C, 0x7E, 0x7E, 0x0C, 0x0C, // 40x7E, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x46, 0x7E, // 50x3C, 0x60, 0x40, 0x7C, 0x66, 0x42, 0x66, 0x3C, // 60x7E, 0x42, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, // 70x3C, 0x66, 0x66, 0x3C, 0x66, 0x42, 0x66, 0x3C, // 80x3C, 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;elsenum = 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 0101elseP2 = 0xFF; // 1111 1111if(!(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~70x7F, 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, // 00x18, 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
};// 段码(未用,可保留)
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; // 允许外部中断1EA = 1; // 总中断允许(一般只需设置一次)
}// 外部中断1服务函数(中断号 2)
void eint1_handler(void) interrupt 2 {P2 = 0x0f; // 可用于调试或 LED 显示if (num == 0)num = 9;elsenum--;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, // 00x18, 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
};// 全局变量
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; // 允许外部中断1EA = 1; // 总中断允许(一般只需设置一次)
}// 外部中断1服务函数(中断号 2)
void eint1_handler(void) interrupt 2 {P2 = 0x0f; // 可用于调试或 LED 显示if (num == 0)num = 9;elsenum--;P2 = ~num; // 显示新的值
}void timer0_init(void)
{TH0 = 0x4C;TL0 = 0x00; //15535/256TMOD &= ~(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, // 00x06, // 10x5B, // 20x4F, // 30x66, // 40x6D, // 50x7D, // 60x07, // 70x7F, // 80x6F // 9
};void timer0_init(void)
{TH0 = 0x4C;TL0 = 0x00; //15535/256TMOD &= ~(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; elseP0 = 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, // 10x5B, // 20x4F, // 30x66, // 40x6D, // 50x7D, // 60x07, // 70x7F, // 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);}