0x00 前言
上文複習了51單片機的基礎模塊,本篇將介紹一些外設,因爲是複習的原因,本文不會涉及,藍橋杯國賽的內容,紅外線,超聲波等會單獨一篇寫出來。
0x01 IIC
參考文章:https://blog.csdn.net/ohy3686/article/details/86716456
在初學的時候會用驅動就可以,因爲是複習,把理論也瞭解以下
以下的理論知識,幾乎都是來自上述文章。(賽高賽高)
基礎知識
IIC總線是PHLIPS公司推出的一種串行總線,是具備多主機系統所需的包括總線裁決和高低速器件同步功能的高性能串行總線.
IIC總線只有兩根雙向信號線。一根是數據線SDA,一根是時鐘線SCL.
IIC總線通過上拉電阻接正電源。當總線空閒時,兩根線均爲高電平。連到總線上的任一器件輸出的低電平,都將使總線的信號變低,連接總線的器件輸出級必須是集電極或漏極開路,以形成線“與”功能。
每個具有IIC接口的設備都有一個唯一的地址,也叫做設備地址。
注意:在多主機系統中,可能同時有幾個主機企圖啓動總線傳送數據。爲了避免混亂, I2C總線要通過總線仲裁,以決定由哪一臺主機控制總線。在80C51單片機應用系統的串行總線擴展中,我們經常遇到的是以80C51單片機爲主機,其它接口器件爲從機的單主機情況。
數據傳送
1.數據位的有效性
I2C總線進行數據傳送時,時鐘信號爲高電平期間,數據線上的數據必須保持穩定,只有在時鐘線上的信號爲低電平期間,數據線上的高電平或低電平狀態才允許變化。
2. 起始和終止信號
SCL線爲高電平期間,SDA線由高電平向低電平的變化表示起始信號;SCL線爲高電平期間,SDA線由低電平向高電平的變化表示終止信號。
起始和終止信號都是由主機發出的,在起始信號產生後,總線就處於被佔用的狀態;在終止信號產生後,總線就處於空閒狀態
連接到I2C總線上的器件,若具有I2C總線的硬件接口,則很容易檢測到起始和終止信號。
接收器件收到一個完整的數據字節後,有可能需要完成一些其它工作,如處理內部中斷服務等,可能無法立刻接收下一個字節,這時接收器件可以將SCL線拉成低電平,從而使主機處於等待狀態。直到接收器件準備好接收下一個字節時,再釋放SCL線使之爲高電平,從而使數據傳送可以繼續進行。
3. 數據傳送格式
在起始信號之後,必須是器件的控制字節,也即是設備地址,其中高4位是器件的類型識別符(EEPROM的識別符爲1010),接着3位是片選信號,最後1位是讀寫控制位,讀操作爲1,寫操作爲0。
小結:對於EEPROM器件來說,如果片選信號爲000,則:
讀操作的設備地址爲:0xA1。
寫操作的設備地址爲:0xA0。
每一個字節必須保證是8位長度。數據傳送時,先傳送最高位(MSB),每一個被傳送的字節後面都必須跟隨一位應答位(即一幀共有9位)
IC總線協議規定,每傳送一個字節數據後,都要有一個應答信號,以確定數據傳送是否被對方收到。應答信號由接收方在數據開始後的第9個時鐘週期發送,在SCL爲高電平期間,接收方將SDA拉爲低電平產生應答,用來結束一個字節的傳輸。也就是說,一幀完整的數據共有9位。
注意:當主機接收數據(也就是在讀數據狀態)時,它收到最後一個字節後,必須向從機發出一個結束傳送的信號。這個信號是通過對從機的“非應答信號”來實現的,在SCL爲高電平期間,SDA爲高電平,即從機釋放SDA線,允許主機產生一個停止信號。
4. 傳輸方式
I2C總線上傳送的數據信號是廣義的,既包括地址信號,又包括真正的數據信號。
在起始信號後必須傳送一個從機的地址(7位),第8位是數據的傳送方向位(R/T),用“0”表示主機發送數據(T),“1”表示主機接收數據(R)。每次數據傳送總是由主機產生的終止信號結束。但是,若主機希望繼續佔用總線進行新的數據傳送,則可以不產生終止信號,馬上再次發出起始信號對另一從機進行尋址。
組合方式:
a、主機向從機發送數據,數據傳送方向在整個傳送過程中不變:
注:有陰影部分表示數據由主機向從機傳送,無陰影部分則表示數據由從機向主機傳送。 A表示應答, A非表示非應答(高電平)。S表示起始信號,P表示終止信號。。
b、主機在第一個字節後,立即從從機讀數據
c、在傳送過程中,當需要改變傳送方向時,起始信號和從機地址都被重複產生一次,但兩次讀/寫方向位正好反相
代碼:
起始信號:
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
Delay_IIC(5);
SDA = 0; //在SCL高電平期間,SDA由高變低
Delay_IIC(5);
SCL = 0;
}
停止信號:
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
Delay_IIC(5);
SDA = 1; //在SCL高電平期間,SDA由高變低
Delay_IIC(5);
}
產生應答:
void IIC_Ack(unsigned char ackbit)
{
if(ackbit)
SDA = 0; //產生應答信號
else
SDA = 1; //產生非應答信號
Delay_IIC(5);
SCL = 1;
Delay_IIC(5); //第9個時鐘週期
SCL = 0;
SDA = 1; //釋放SDA線
Delay_IIC(5);
}
等待應答:
bit IIC_WaitAck(void)
{
SDA = 1;
Delay_IIC(5);
SCL = 1;
Delay_IIC(5);
if(SDA) //在SCL高電平期間,SDA爲高電平,從機非應答。
{
SCL = 0;
IIC_Stop();
return 0;
}
else //在SCL高電平期間,SDA爲低電平,從機有應答。
{
SCL = 0;
return 1;
}
}
發送數據:
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0;i<8;i++) //循環發送8位數據
{
if(byt & 0x80) //數據位是高電平
{
SDA = 1;
}
else //數據位是低電平
{
SDA = 0;
}
Delay_IIC(5);
SCL = 1; //SCL高電平期間,SDA的數據要保持穩定
byt <<= 1; //發送的數據左移,準備發送下一位
Delay_IIC(5); //等待SDA的數據被讀取
SCL = 0;
}
}
接收數據:
unsigned char IIC_RecByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++)
{
SCL = 1;
Delay_IIC(5); //在SCL高電平期間,讀取SDA的數據
da <<= 1;
if(SDA)
da |= 0x01;
SCL = 0;
Delay_IIC(5);
}
return da;
}
0x02 AT24C02
理論的地方主要了解,關鍵是用
電路
WP 寫保護 如果WP 管腳連接到 Vcc 所有的內容都被寫保護,只能讀。當 WP 管腳連接到Vss或懸空,允許器件進行正常的讀/寫操作。
器件尋址
24C02的設備地址包括固定部分和可編程部分。可編程部分需要根據硬件引腳A0、A1和A2來設置。設備地址的最後一位用於設置數據傳輸的方向,即讀/寫位。
讀操作的設備地址爲:0xA1。
寫操作的設備地址爲:0xA0。
操作:
1.應答信號:
寫: 24C02在接收到起始信號和從器件地址之後響應一個應答信號,如果器件已選擇了寫操作,則在每接收一個8位字節之後響應一個應答信號;
讀:在發送一個 8 位數據後釋放SDA線並監視一個應答信號,一旦接收到應答信號,24C02繼續發送數據,如主器件沒有發送應答信號器件停止傳送數據且等待一個停止信號。
應答時序:
第一行(主機時鐘),第二行(發送器輸出的數據),第三行(接收器輸出的數據)
2.寫入
字節寫操作:
24C02接收完設備地址後,產生應答信號;然後接收8位內存字節地址,產生應答信號,接着接收一個8位數據,產生應答信號;最後主機發送停止信號,字節寫操作結束。
寫入時序:
頁寫:
24C02可以一次寫入 16 個字節的數據,
頁寫操作的啓動和字節寫一樣,不同在於傳送了一字節數據後並不產生停止信號,主器件被允許再發送 P=15個額外的字節;
注意:每發送一個字節數據後24C02產生一個應答位並將字節地址低位加 1 高位保持不變 ,如果在發送停止信號之前主器件發送超過P+1個字節 地址計數器將自動翻轉,先前寫入的數據被覆蓋。
頁寫時序
3.讀操作
24C02 讀操作的初始化方式和寫操作時一樣,僅把 R/W 位置爲 1 ,有三種不同的讀操作方式:立即地址讀、選擇讀和連續讀。
立即地址讀:
AT24C02的地址計數器內容爲最後操作字節的地址加 1。也就是說,如果上次讀/寫的操作地址爲 N,則立即讀的地址從地址 N+1開始。
選擇性讀:
選擇性讀操作允許主器件對寄存器的任意字節進行讀操作,主器件首先通過發送起始信號、從器件地址和它想讀取的字節數據的地址執行一個僞寫操作。
連續讀:
連續讀操作可通過立即讀或選擇性讀操作啓動,在24C02發送完一個 8 位字節數據後,主器件產生一個應答信號來響應,告知24C02主器件要求更多的數據,對應每個主機產生的應答信號,24C02將發送一個 8 位數據字節,當主器件不發送應答信號而發送停止位時結束此操作。
代碼
單片機向24C02的add地址上,寫入char型的數dat
void wrbyte_24c02(unsigned char add,unsigned char dat)
{
// Device Address 1010 000 R/W
iic_start();
iic_sendbyte(0xa0);
iic_waitack();
iic_sendbyte(add);
iic_waitack();
iic_sendbyte(dat);
iic_waitack();
iic_stop();
delay(10);
}
寫入多個char型的數
void wrbyte_24c02(uchar add, uchar dat0, uchar dat1,……)
{
// Device Address 1010 000 R/W
iic_start();
iic_sendbyte(0xa0);
iic_waitack();
iic_sendbyte(add);
iic_waitack();
iic_sendbyte(dat0);
iic_waitack();
iic_sendbyte(dat1);
iic_waitack();
… …
iic_stop();
delay(10);
}
單片機從24C02的add地址上讀取一個數
unsigned char rdbyte_24c02(unsigned char add)
{
// Device Address 1100 000 R/W
unsigned char da;
iic_start();
iic_sendbyte(0xa0);
iic_waitack();
iic_sendbyte(add);
iic_waitack();
iic_start();
iic_sendbyte(0xa1);
iic_waitack();
da = iic_recbyte();
iic_ack(0);
iic_stop();
return da;
}
讀取多個:
unsigned char da[]={0,0,0, … … };
iic_start();
iic_sendbyte(0xa0);
iic_waitack();
iic_sendbyte(add);
iic_waitack();
iic_start();
iic_sendbyte(0xa1);
iic_waitack();
da[0] = iic_recbyte();
iic_ack(1);
da[1] = iic_recbyte();
iic_ack(1);
… …
da[n] = iic_recbyte();
iic_ack(0);
iic_stop();
return da;
}
0x03 DS18B20
參考文章:https://www.cnblogs.com/ALittleBee/p/9427165.html
單總線數字溫度傳感器
DS18B20的基本驅動的編寫,不在這裏講述,直接拿來用,感興趣的看官可以自己探究。
1. 基本知識
DS18B20的庫文件,裏面有傳感器復位、寫字節和讀字節三個函數。基本操作流程要知道。
執行序列:
1.初始化
2.ROM操作指令(識別有多少隻,什麼型號的DS18B20掛在總線上,識別哪些器件符合報警條件,定位要操作的DS18B20等)
3.DS18B20功能指令
每一次DS18B20的操作都必須滿足以上步驟
初始化不用說了,說一下rom指令和功能指令,紅色是常用的
ROM指令
1.[F0h] 搜索(多隻)
2.[33h] 讀取(單隻)
3.[55h] 匹配
4.[CCh] 忽略
5.[ECh] 報警搜索
功能指令:
1.[44h] 溫度轉換指令
2.[4Eh] 寫暫存器指令
3.[BEh] 讀暫存器指令
4.[48h] 拷貝暫存器指令
5.[B8h] 召回EEPROM指令
6.[B4h] 讀電源模式指令
2. 基本操作
(來自小蜜蜂老師)
<1> 主機對DS18B20進行復位初始化。
<2> 主機向DS18B20寫0xCC命令,跳過ROM。
<3> 主機向DS18B20寫0x44命令,開始進行溫度轉換。
<4> 等待溫度轉換完成。
<5> 主機對DS18B20進行復位初始化。
<6> 主機向DS18B20寫0xCC命令,跳過ROM。
<7> 主機向DS18B20寫0xBE命令,依次讀取DS18B20發出的從第0一第8,共九個字節的數據。如果只想讀取溫度數據,那在讀完第0和第1個數據後就不再理會後面DS18B20發出的數據即可,或者通過DS18B20復位,停止數據的輸出。
代碼:
uint temperature(void)
{
uint temp;
uchar HI_temp,LOW_temp;
Init_DS18B20(); //DS18B20初始化
Write_DS18B20(0xcc);//跳過ROM
Write_DS18B20(0x44);//溫度轉換
delay(1000); //延時等待
Init_DS18B20();
Write_DS18B20(0xcc);//跳過ROM
Write_DS18B20(0xbe);//讀溫度命令
LOW_temp = Read_DS18B20();//讀低8位
HI_temp = Read_DS18B20(); //讀高8位
temp = HI_temp;
temp = temp << 8;
temp |= LOW_temp;
temp = temp * 0.0625 * 100;
return temp;
}
注意需要×0.0625:
從DS18B20讀取的二進制必須先轉換成十進制,才能用於字符的現實,DS18B20的轉換精度爲9~12位可選,爲了提高精度採用12位,在採用12位轉換精度是,溫度寄存器裏的值是以0.062爲步進的,即溫度值爲溫度寄存器裏的二進制值乘以0.0625,就是實際的十進制溫度值。12位的最低位爲權爲1/16,即0.0625
0x04 PWM
這個要涉及AD DA轉化的問題。
具體原理可以看這兩個普中官方的視頻:
https://www.bilibili.com/video/BV1h4411y7Gk?p=41
https://www.bilibili.com/video/BV1h4411y7Gk?p=42
因爲篇幅原因,就不再贅述了,在51中,主要就是改變佔空比
代碼
直接用代碼,來講述
1. 小蜜蜂,藍橋杯的例題
代碼:
#include <REGX52.H>
typedef unsigned char u8;
typedef unsigned int u16;
sbit S7 = P3^0;
sbit L1 = P0^0;
void Delay(u16 t)
{
while(t--);
}
void InitHc138(u8 n)
{
switch (n)
{
case 4:
P2 = (P2 & 0X1F) | 0X80;
break;
case 5:
P2 = (P2 & 0X1F) | 0Xa0;
break;
case 6:
P2 = (P2 & 0X1F) | 0Xc0;
break;
case 7:
P2 = (P2 & 0X1F) | 0Xe0;
break;
}
}
/////////////////////////////---¶¨Ê±Æ÷---////////////////////////
u8 count_T0 = 0;
u8 PWN_duty = 0;
void InitT0()//0.1ms * 100 = 10ms
{
TMOD = 0X0010;//×Ô¶¯ÖØ×°ÔØ
TH0 = (65535 - 100) / 256;
TL0 = (65535 - 100) % 256;
EA = 1;
ET0 = 1;
}
void Service_T0() interrupt 1
{
count_T0++;
if(count_T0 <= PWN_duty)
{
L1 = 0;
}
else if(count_T0 < 100)
{
L1 = 1;
}
else if(count_T0 == 100)
{
L1 = 0 ;
count_T0 = 0;
}
}
/////////////////////////////--End_T0--//////////////////////////
u8 stat = 0;
void Scan_Key()
{
if(S7 == 0)
{
Delay(100);
if(S7 == 0)
{
switch (stat)
{
case 0:
TR0 = 1;
L1 = 0;
PWN_duty = 10;
stat = 1;
break;
case 1:
PWN_duty = 50;
stat = 2;
break;
case 2:
PWN_duty = 90;
stat = 3;
break;
case 3:
L1 = 1;
PWN_duty = 0;
TR0 = 0;
stat = 0;
break;
}
while(S7 == 0);
/////////
}
}
}
void main()
{
InitHc138(4);
L1 = 1;
InitT0();
while(1)
{
Scan_Key();
}
}
2. 呼吸燈
https://blog.csdn.net/ohy3686/article/details/88372244
總結
暫時先總結3個外設,還有DS1302時鐘芯片,紅外通信,等外設,因爲複習考核的緣故,等博主考覈完再來補上。下一篇複習一下 硬件知識。