九:串口通信
通信:无线和有线
单工 半双工 全双工
并行:多个数据线 串行:一根数据线
同步:通信双方使用同一个时钟,SPI信息帧,有CLK引脚
异步:通信双方使用不同时钟,双方要固定的数据帧(0起始,1停止)和传输速度(波特率,一般都是9600bps = 1s:单位时间传输了多少个码元,这里用二进制码元),无CLK引脚
空闲时总线保持高电平。起始信号:由高到低 起始信号:由低到高
数据位:5,6,7,8位 需要起始位:1~2位
校验位:奇,偶,无 //一般自定义校验规则
停止信号:由低到高 停止位:1~2位
eg:11001100 实际数据:0x33,数据头是低位
一:测试发送和接受
//定时器0
#include <reg51.h>unsigned char recv = 0;
// 硬件接口定义
sbit RCLK = P3^5;
sbit SRCLK = P3^6;
sbit SER = P3^4;// 延时函数(简易版)
void delayn(unsigned char n) {while(n--);
}unsigned char code digit_table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};// 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, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00, 0x01, 0x7f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x45, 0x45, 0x45, 0x27, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x22, 0x00, 0x00,0x00, 0x04, 0x7f, 0x24, 0x14, 0x0c, 0x00, 0x00,0x00, 0x4e, 0x51, 0x51, 0x51, 0x72, 0x00, 0x00, 0x00, 0x26, 0x49, 0x49, 0x49, 0x3e, 0x00, 0x00,0x00, 0x70, 0x4f, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00,0x00, 0x3e, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00
};unsigned char num = 0; void dispaly_num(unsigned char num)
{unsigned char i = 0; for(i = 0; i < 8; i++){write_74hc595(1 << (7 - i));P0 = ~h_data[i + num * 8];delayn(100);P0 = 0xff;}
}#if 0
void serial_server() interrupt 4
{if(TI)TI = 0;//清 发送中断标志if(RI)RI = 0;//清 发送中断标志recv = SBUF;//接受串口数据switch(recv){case 0:num = 0; P0 = digit_table[0];break;case 1:num = 1; P0 = digit_table[1];break;case 2:num = 2; P0 = digit_table[2];break;case 3:num = 3; P0 = digit_table[3];break;case 4:num = 4; P0 = digit_table[4];break;case 5:num = 5; P0 = digit_table[5];break;case 6:num = 6; P0 = digit_table[6];break;case 7:num = 7; P0 = digit_table[7];break;case 8:num = 8; P0 = digit_table[8];break;case 9:num = 9; P0 = digit_table[9];break;default: SBUF = 0X0E;P0 = digit_table[10];break;}}
#endifvoid uart_init(void)
{SCON = 0x50; TMOD &= 0x0F; TMOD |= 0x20; TL1 = 0xFD; TH1 = 0xFD; ES = 1;EA = 1;ET1 = 0; TR1 = 1;}void uart_send(unsigned char ch)
{TI = 0;SBUF = ch;while(!TI);
}unsigned char uart_recv(void)
{unsigned char ch = 0;while(!RI);ch = SBUF;RI = 0;return ch;
}// 主函数
void main(void)
{unsigned char ch = 0;uart_init();while(1){ch = uart_recv();ch += 1;uart_send(ch);}
#if 0uart_init();while(1){dispaly_num(num);}
#endif
}
//如果不用中断的话,只有端口一直发,数码管才能呢个显示,但是是频闪状态,和中断相比,缺点很大
#include <reg51.h>unsigned char recv = 0;
// 硬件接口定义
sbit RCLK = P3^5;
sbit SRCLK = P3^6;
sbit SER = P3^4;// 延时函数(简易版)
void delayn(unsigned char n) {while(n--);
}unsigned char code digit_table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};// 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, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00, 0x01, 0x7f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x45, 0x45, 0x45, 0x27, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x22, 0x00, 0x00,0x00, 0x04, 0x7f, 0x24, 0x14, 0x0c, 0x00, 0x00,0x00, 0x4e, 0x51, 0x51, 0x51, 0x72, 0x00, 0x00, 0x00, 0x26, 0x49, 0x49, 0x49, 0x3e, 0x00, 0x00,0x00, 0x70, 0x4f, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00,0x00, 0x3e, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00
};unsigned char num = 0; void dispaly_num(unsigned char num)
{unsigned char i = 0; for(i = 0; i < 8; i++){write_74hc595(1 << (7 - i));P0 = ~h_data[i + num * 8];delayn(100);P0 = 0xff;}
}#if 0
void serial_server() interrupt 4
{if(TI)TI = 0;//清 发送中断标志if(RI)RI = 0;//清 发送中断标志recv = SBUF;//接受串口数据switch(recv){case 0:num = 0; P0 = digit_table[0];break;case 1:num = 1; P0 = digit_table[1];break;case 2:num = 2; P0 = digit_table[2];break;case 3:num = 3; P0 = digit_table[3];break;case 4:num = 4; P0 = digit_table[4];break;case 5:num = 5; P0 = digit_table[5];break;case 6:num = 6; P0 = digit_table[6];break;case 7:num = 7; P0 = digit_table[7];break;case 8:num = 8; P0 = digit_table[8];break;case 9:num = 9; P0 = digit_table[9];break;default: SBUF = 0X0E;P0 = digit_table[10];break;}}
#endifvoid uart_init(void)
{SCON = 0x50; TMOD &= 0x0F; TMOD |= 0x20; TL1 = 0xFD; TH1 = 0xFD; ES = 1;EA = 1;ET1 = 0; TR1 = 1;}void uart_send(unsigned char ch)
{TI = 0;SBUF = ch;while(!TI);
}unsigned char uart_recv(void)
{unsigned char ch = 0;while(!RI);ch = SBUF;RI = 0;return ch;
}// 主函数
void main(void)
{unsigned char ch = 0;uart_init();while(1){ch = uart_recv();ch += 1;uart_send(ch);if(ch < 10){dispaly_num(ch);}}
#if 0uart_init();while(1){dispaly_num(num);}
#endif
}
二:UART串口
STC89C51RC/RD+系列单片机内部集成有一个功能很强的全双工串行通信口,与传统8051单片机的串口完全兼容。设有2个互相独立的接收、发送缓冲器,可以同时发送和接收数据。发送缓冲器只能写入而不能读出,接收缓冲器只能读出而不能写入,,因而两个缓冲器可以共用一个地址码(99H)。两个缓冲器统称串行通信特殊功能寄存器SBUF。
STC89C51RC/RD+系列单片机串行口对应的硬件部分对应的管脚是P3.0/RxD和P3.1/TxD。
#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;break;}}void main(void)
{
#if 1 SCON = 0x50;//设置串口工作方式,8位数据,可变波特率 TMOD &= 0x0F; TMOD |= 0x20;//设置T1工作方式 TL1 = 0xFD; TH1 = 0xFD;//设置9600波特率 ES = 1;//开 串口中断 //使能端EA = 1;//开 总中断 TR1 = 1;//启动定时器1
#endif//P0 = 0xEF; while(1);}
三:通过串口通信修改led点阵数字
//定时器0
#include <reg51.h>unsigned char recv = 0;
// 硬件接口定义
sbit RCLK = P3^5;
sbit SRCLK = P3^6;
sbit SER = P3^4;// 延时函数(简易版)
void delayn(unsigned char n) {while(n--);
}unsigned char code digit_table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};// 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, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00, 0x01, 0x7f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x45, 0x45, 0x45, 0x27, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x22, 0x00, 0x00,0x00, 0x04, 0x7f, 0x24, 0x14, 0x0c, 0x00, 0x00,0x00, 0x4e, 0x51, 0x51, 0x51, 0x72, 0x00, 0x00, 0x00, 0x26, 0x49, 0x49, 0x49, 0x3e, 0x00, 0x00,0x00, 0x70, 0x4f, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00,0x00, 0x3e, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00
};unsigned char num = 0; void dispaly_num(unsigned char num)
{unsigned char i = 0; for(i = 0; i < 8; i++){write_74hc595(1 << (7 - i));P0 = ~h_data[i + num * 8];delayn(100);P0 = 0xff;}
}void serial_server() interrupt 4
{if(TI)TI = 0;if(RI)RI = 0;recv = SBUF;switch(recv){case '0':num = 0; P0 = digit_table[0];break;case '1':num = 1; P0 = digit_table[1];break;case '2':num = 2; P0 = digit_table[2];break;case '3':num = 3; P0 = digit_table[3];break;case '4':num = 4; P0 = digit_table[4];break;case '5':num = 5; P0 = digit_table[5];break;case '6':num = 6; P0 = digit_table[6];break;case '7':num = 7; P0 = digit_table[7];break;case '8':num = 8; P0 = digit_table[8];break;case '9':num = 9; P0 = digit_table[9];break;}}// 主函数
void main(void)
{SCON = 0x50; TMOD &= 0x0F; TMOD |= 0x20; TL1 = 0xFD; TH1 = 0xFD; ES = 1;EA = 1; TR1 = 1;while(1){dispaly_num(num);}
}
四:总测试
//定时器0
#include <reg51.h>unsigned char recv = 0;
unsigned char recv_flag = 0;
// 硬件接口定义
sbit RCLK = P3^5;
sbit SRCLK = P3^6;
sbit SER = P3^4;// 延时函数(简易版)
void delayn(unsigned char n) {while(n--);
}void delay_ms(unsigned int num)
{unsigned char i,j;while(num--){i = 2;//看具体晶振大小j = 199;}do{while(--j);}while(--i);
}unsigned char code digit_table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};// 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, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00, 0x01, 0x7f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x45, 0x45, 0x45, 0x27, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x22, 0x00, 0x00,0x00, 0x04, 0x7f, 0x24, 0x14, 0x0c, 0x00, 0x00,0x00, 0x4e, 0x51, 0x51, 0x51, 0x72, 0x00, 0x00, 0x00, 0x26, 0x49, 0x49, 0x49, 0x3e, 0x00, 0x00,0x00, 0x70, 0x4f, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00,0x00, 0x3e, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00
};unsigned char num = 0; void dispaly_num(unsigned char num)
{unsigned char i = 0; for(i = 0; i < 8; i++){write_74hc595(1 << (7 - i));P0 = ~h_data[i + num * 8];delayn(100);P0 = 0xff;}
}#if 0
void serial_server() interrupt 4
{if(TI)TI = 0;//清 发送中断标志if(RI)RI = 0;//清 发送中断标志recv = SBUF;//接受串口数据switch(recv){case 0:num = 0; P0 = digit_table[0];break;case 1:num = 1; P0 = digit_table[1];break;case 2:num = 2; P0 = digit_table[2];break;case 3:num = 3; P0 = digit_table[3];break;case 4:num = 4; P0 = digit_table[4];break;case 5:num = 5; P0 = digit_table[5];break;case 6:num = 6; P0 = digit_table[6];break;case 7:num = 7; P0 = digit_table[7];break;case 8:num = 8; P0 = digit_table[8];break;case 9:num = 9; P0 = digit_table[9];break;default: SBUF = 0X0E;P0 = digit_table[10];break;}}
#endifvoid uart_init(void)
{SCON = 0x50; TMOD &= 0x0F; TMOD |= 0x20; TL1 = 0xFD; TH1 = 0xFD; ES = 1; EA = 1;ET1 = 0; TR1 = 1;}#if 1
void uart_send(unsigned char ch)
{TI = 0;//上来就TI = 0 就是为了保险,防止第一次的值不是默认0; SBUF = ch;while(!TI);//TI = 0;//如果用flag的话,那就还需要清零,不然会一直闪 卡在while
}unsigned char uart_recv(void)
{unsigned char ch = 0;while(!RI);ch = SBUF;RI = 0;return ch;
}
#endif#if 1
void test() interrupt 4 //中断必须是空函数
{
#if 0unsigned char ch = 0;ch = uart_recv(); //函数放进中断-------阻塞//如果要自定义协议的话,可以把recv[i],当作数组还用,数据输入结束后,统一把数组纺发上去/收过来uart_send(ch); //uart_recv() 和 uart_send() 是阻塞函数://它们会 等待 RI/TI,但中断里 不应该阻塞if (ch >= '0' && ch <= '9') num = ch - '0';
#endifunsigned char ch; if (RI) {RI = 0; //接收到,清零recv_flag = 1; ch = SBUF; SBUF = ch; if (ch >= '0' && ch <= '9') num = ch - '0'; }if (TI) // 发送中断{TI = 0; // 清除标志}#if 0switch(ch){case 0:num = 0; P0 = digit_table[0];break;case 1:num = 1; P0 = digit_table[1];break;case 2:num = 2; P0 = digit_table[2];break;case 3:num = 3; P0 = digit_table[3];break;case 4:num = 4; P0 = digit_table[4];break;case 5:num = 5; P0 = digit_table[5];break;case 6:num = 6; P0 = digit_table[6];break;case 7:num = 7; P0 = digit_table[7];break;case 8:num = 8; P0 = digit_table[8];break;case 9:num = 9; P0 = digit_table[9];break;default: SBUF = 0X0E;P0 = digit_table[10];}
#endif
}
#endif// 主函数
void main(void)
{
#if 0unsigned char ch = 0;uart_init();while(1){ch = uart_recv();//ch += 1;//delay_ms(65534);uart_send(ch);if(ch < 10){dispaly_num(ch);}}
#endif
#if 1uart_init();while(1){if(recv_flag) //会一直闪{recv_flag = 0;}dispaly_num(num);}
#endif
#if 0uart_init();while(1){dispaly_num(num);}
#endif
}
十:DS18B20
一:单总线时序结构
初始化:主机将总线拉低至少480us,然后释放总线,等待15~60us后,
存在的 从机 会拉低总线60-240us以响应 主机 ,之后 从机 将释放总线
下图为发送一位和接受一位的示意图:
前提:外接电源,且只有一个DS18B20
发送一位:
主机将总线拉低60~120us,然后释放总线,表示发送0;
主机将总线拉低1~15us,然后释放总线,表示发送1。
从机将在总线拉低30us后(典型值)(30us相当于:主机发送数据后,缓一会,丛机再读取)读取电平,整个时间片应大于60us
接收一位:
主机将总线拉低1~15us,然后释放总线(如果还是低电平,就是丛机还在拉低,如果上升高电平,说明从机没有动作),并在拉低后15us内读取总线电平(尽量贴近15us的末尾),
读取为低电平则为接收0,读取为高电平则为接收1,整个时间片应大于60us
发送一个字节:
连续调用8次发送一位的时序,依次发送一个字节的8位 (低位在前) 0 1 2 3 4 5 6 7
接收一个字节:
连续调用8次接收一位的时序,依次接收一个字节的8位 (低位在前)
二:DS18B20操作流程
初始化:从机复位,庄机判断从机是否响应
ROM操作:ROM指令+本指令需要的读写操作
功能操作:功能指令+本指令需要的读写操作
三:数据帧(前提:外部供电且只有一个)
跳过ROM:因为只有一个温度传感,不需要发特定的数字来识别对应的温度传感
温度变换:初始化一跳过ROM→开始温度变换
温度读取:初始化→跳过ROM→[读暂存器(连续的读操作)]
就为了读取温度数据,就读取前两位就行
四:温度存储格式
一共16位,前8位是符号,后8位是数值大小
如果是负数,那就要用补码来导出实际数值
五:ROM操作流程
十一:AD/DA
一:基础概念
AD(Analog to Digital):模拟-数字转换,将模拟信号转换为计算机可操作的数字信号
DA(Digital to Analog):数字-模拟转换,将计算机输出的数字信号转换为模拟信号
AD/DA转换打开了计算机与模拟信号的大门,极大的提高了计算机系统的应用范围,也为模拟信号数字化处理提供了可能
模拟量:0~5v
数字量:0~255
AD转换通常有多个输入通道,用多路选择开关连接至AD转换器以实现AD多路复用的目的,提高硬件利用率
AD/DA与单片机数据传送可使用并口(速度快、原理简单),也可使用串口 (接线少、使用方便)
可将AD/DA模块直接集成在单片机内,这样直接写入/读出寄存器就可进行AD/DA转换,单片机的IO口可直接复用为AD/DA的通道