九:串口通信
通信:無線和有線
? 單工 半雙工 全雙工
并行:多個數據線 串行:一根數據線
同步:通信雙方使用同一個時鐘,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的通道