STM32之IO口模擬IIC


本文介紹如何使用STM32標准外設庫的GPIO端口模擬IIC,本例程使用PB6和PB7模擬一路IIC。

本文適合對單片機及C語言有一定基礎的開發人員閱讀,MCU使用STM32F103VE系列。

 

1.    簡介

IIC (Inter-Integrated Circuit)總線,也可寫作I2C,是PHILIPS 公司開發的兩線式串行總線,用於多設備之間通訊,分為主機Master和從機Slave,主機和從機可以有多個,但一般情況下只有一個主機,從機之間可以通過地址進行區分,不同種類的設備地址不同,如果同時接入多個相同種類的設備,可以通過片選信號對從機進行選擇。通訊只能由主機發起,支持的操作分為讀取和寫入,即主機讀取從機的數據,以及向從機寫入數據。

I2C兩線分別是時鍾線SCL和數據線SDA,其中SCL和SDA均由主機控制,可以設置成開漏輸出模式。

2.    協議說明

2.1. 總線傳輸信號

  • 空閑狀態:IIC空閑狀態時SCL和SDA均輸出高電平,初始狀態以及發送結束信號之后均為空閑狀態。
  • 開始信號:START,簡寫S,SCL為高電平,SDA由高電平向低電平跳變。
  • 結束信號:STOP,簡寫P,SCL為高電平,SDA由低電平向高電平跳變。
  • 從機地址:SLAVE_ADDRESS,每種從機都有一個表示該設備的地址,地址一般為7位,主機發起通訊時,通過 SDA 信號線發送設備地址(SLAVE_ADDRESS)來查找從機,緊跟設備地址的一個數據位用來表示數據傳輸方向(R/W位),數據方向位為“1”時表示主機由從機讀數據,數據方向位為“0”時表示主機向從機寫數據。從機接收到匹配的地址后,會返回一個應答(ACK)信號,只有接收到應答信號后,主機才能繼續發送或接收數據。
  • 響應信號:ACK/NACK,簡寫A,響應包括應答(ACK)和非應答(NACK),當數據接收端時,當設備(無論主從機)接收到 I2C 傳輸的一個字節數據或地址后,若希望對方繼續發送數據,則需要向對方發送“應答(ACK)”信號,即SDA為低電平,發送方會繼續發送下一個數據;若接收端希望結束數據傳輸,則向對方發送“非應答(NACK)”信號,即SDA為高電平,發送方接收到該信號后會產生一個停止信號,結束信號傳輸。
  • 主機寫入:SCL為高電平時,SDA有效,SDA為輸出模式,主機控制SDA輸出高電平時表示寫入1,SDA輸出低電平時表示寫入0。
  • 主機讀取:SCL為高電平時,SDA有效,SDA為輸入模式,主機讀取SDA高電平時表示輸入1,SDA低電平時表示輸入0。此時主機釋放對 SDA 信號線的控制,由從機控制 SDA 信號線,主機接收信號。

2.2. 基本讀寫過程

  • 寫數據:主機先發一個開始信號(S),然后發送從機地址(SLAVE ADDRESS),后續跟上寫信號(R/W位為0),然后等待從機的應答信號(ACK位為0),主機向從機傳輸數據(DATA),數據包為1個字節共8位,從高位到低位依次發送,主機每發送完1個字節數據,都要等待從機的應答信號(ACK),重復這個過程,可以向從機傳輸 N 個數據,當數據傳輸結束時,主機向從機發送一個停止傳輸信號(P),表示不再傳輸數據。
  • 讀數據:主機先發一個開始信號(S),然后發送從機地址(SLAVE ADDRESS),后續跟上讀信號(R/W位為1),然后等待從機的應答信號(ACK位為0),主機從從機讀取數據(DATA),數據包為1個字節共8位,從高位到低位依次發送,從機每發送完1個字節數據,都要等待主機的應答信號(ACK),重復這個過程,可以從從機讀取 N 個數據,當主機希望停止接收數據時,就向從機返回一個非應答信號(NACK),則從機自動停止數據傳輸,主機再向從機發送一個停止傳輸信號(P),表示不再傳輸數據。
  • 讀和寫數據:除了基本的讀寫,I2C 通訊還有一種是復合讀寫模式,該傳輸過程有兩次起始信號(S)。一般在第一次傳輸中,主機通過 SLAVE_ADDRESS 尋找到從設備后,發送一段“數據”,這段數據通常用於表示從設備內部的寄存器或存儲器地址(注意區分它與 SLAVE_ADDRESS 的區別);在第二次的傳輸中,對該地址的內容進行讀或寫。也就是說,第一次通訊是告訴從機讀寫地址,第二次則是讀寫的實際內容。

  

2.3. 速度模式

標准模式傳輸速率為100kbit/s,即10us可以傳輸一個bit,如果用GPIO模擬I2C時電平變換時需要增加適當的延時。

3.    初始化

初始化跟普通GPIO類似,只是輸出模式設置為開漏輸出。

其中SCL始終輸出信號,但SDA需要支持輸出信號和讀取信號,當設置為開漏輸出時,如果需要輸出信號,則正常輸出即可,如果需要讀取信號,則MCU將SDA輸出高電平,此時如果從機輸出低電平,則SDA被拉低,此時MCU可以讀到低電平。即GPIO引腳為開漏輸出模式時,MCU輸出高電平時,即釋放了該引腳的控制,此時該引腳的電平取決於從機的輸出,且MCU仍可以讀取該引腳的電平。

GPIO初始化完成之后,可以將SCL和SDA置為高電平,即釋放該引腳的控制,如果總線上有多個主機,則不會干擾其他設備的通訊。

4.    信號模擬

需要按照通訊信號的時序,實現START、STOP、ACK、NACK、Read、Write和WaitAck信號。

 

完整代碼(僅自己編寫的部分)

  1 #define IIC_SCL_1    GPIO_SetBits(GPIOB, GPIO_Pin_6)            /* SCL = 1 */
  2 #define IIC_SCL_0    GPIO_ResetBits(GPIOB, GPIO_Pin_6)        /* SCL = 0 */
  3 
  4 #define IIC_SDA_1    GPIO_SetBits(GPIOB, GPIO_Pin_7)            /* SDA = 1 */
  5 #define IIC_SDA_0    GPIO_ResetBits(GPIOB, GPIO_Pin_7)        /* SDA = 0 */
  6 
  7 #define IIC_READ_SDA()    GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7)    /* 讀SDA口線狀態 */
  8 
  9 //初始化IIC
 10 void IIC_Init(void)
 11 {                         
 12     GPIO_InitTypeDef GPIO_InitStructure;
 13 
 14     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);    
 15        
 16     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
 17     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ;   //開漏輸出
 18     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 19     GPIO_Init(GPIOB, &GPIO_InitStructure);
 20  
 21     IIC_Stop();
 22 }
 23 
 24 //產生IIC起始信號
 25 //SCL為高電平時SDA由高變低
 26 /*
 27 SCL: ̄ ̄ ̄ ̄ ̄\_
 28 SDA: ̄ ̄\____
 29 */
 30 void IIC_Start(void)
 31 {
 32     IIC_SDA_1;
 33     IIC_SCL_1;
 34     delay_us(4);
 35      IIC_SDA_0;
 36     delay_us(4);
 37     IIC_SCL_0;
 38 }      
 39 
 40 //產生IIC停止信號
 41 //SCL為高電平時SDA由低變高
 42 //IIC空閑時SCL和SDA均輸出高電平,這樣不會干擾其他設備的收發
 43 /*
 44 SCL: ̄ ̄ ̄ ̄
 45 SDA:__/ ̄
 46 */
 47 void IIC_Stop(void)
 48 {
 49     IIC_SDA_0;
 50     IIC_SCL_1;
 51     delay_us(4);
 52     IIC_SDA_1;
 53 }
 54 
 55 //等待應答信號到來
 56 //返回值:1,接收應答失敗
 57 //        0,接收應答成功
 58 uint8_t IIC_WaitAck(void)
 59 {
 60     uint8_t errCount = 0;
 61     uint8_t ack = 0;
 62     
 63     IIC_SDA_1;
 64     delay_us(4);
 65     IIC_SCL_1;
 66     delay_us(4);     
 67 
 68     while(IIC_READ_SDA())
 69     {
 70         errCount++;
 71         if(errCount > 250){
 72             ack = 1;
 73             break;
 74         }
 75     }
 76     IIC_SCL_0;
 77     
 78     return ack;
 79 } 
 80 
 81 //產生應答ACK
 82 //SCL為高電平時SDA為低電平表示應答
 83 /*
 84 SCL:     ̄ ̄\____
 85 SDA:_______/ ̄
 86 */
 87 void IIC_Ack(void)
 88 {
 89     IIC_SDA_0;
 90     delay_us(4);
 91     IIC_SCL_1;
 92     delay_us(4);
 93     IIC_SCL_0;
 94     delay_us(4);
 95     IIC_SDA_1;        //釋放SDA
 96 }
 97 
 98 //產生非應答NACK
 99 //SCL為高電平時SDA為高電平表示非應答
100 /*
101 SCL:     ̄ ̄\__
102 SDA: ̄ ̄ ̄ ̄ ̄ ̄ ̄
103 */
104 void IIC_NAck(void)
105 {
106     IIC_SDA_1;
107     delay_us(4);
108     IIC_SCL_1;
109     delay_us(4);
110     IIC_SCL_0;
111     delay_us(4);
112 }        
113 
114 //IIC發送一個字節
115 /*
116 SCL:_  _/ ̄\__  _/ ̄ ̄\__  _/ ̄ ̄\__  _/ ̄ ̄\__  _/ ̄ ̄\__  _/ ̄ ̄\__  _/ ̄ ̄\__  _/ ̄ ̄\__
117 SDA:--  ------------  --------------  --------------  --------------  --------------  --------------  --------------  --------------
118 */
119 void IIC_WriteByte(uint8_t txd)
120 {                        
121     uint8_t i;   
122 
123     IIC_SCL_0;
124     for(i = 0; i < 8; i++)
125     {   
126         (txd & 0x80) ? IIC_SDA_1 : IIC_SDA_0;
127         txd <<= 1;
128 
129         delay_us(4);
130         IIC_SCL_1;
131         delay_us(4);
132         IIC_SCL_0;
133         delay_us(4);
134     }
135     IIC_SDA_1;
136 }        
137 
138 //讀1個字節,ack=1時,發送ACK,ack=0,發送NACK
139 /*
140 SCL: ̄ ̄\__  / ̄ ̄\__  / ̄ ̄\__  / ̄ ̄\__  / ̄ ̄\__  / ̄ ̄\__  / ̄ ̄\__  / ̄ ̄\__
141 SDA:==========  ============  ============  ============  ============  ============  ============  ============  
142 */
143 uint8_t IIC_ReadByte(uint8_t ack)
144 {
145     uint8_t i, rcv = 0;
146     
147     for(i = 0; i < 8; i++)
148     {
149         rcv <<= 1;
150         IIC_SCL_1;
151         delay_us(4); 
152         if(IIC_READ_SDA()){
153             rcv++;   
154         }
155         IIC_SCL_0; 
156         delay_us(4);
157     }
158     
159     ack ? IIC_Ack() : IIC_NAck();
160     
161     return rcv;
162 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM