日本a√视频在线,久久青青亚洲国产,亚洲一区欧美二区,免费g片在线观看网站

        <style id="k3y6c"><u id="k3y6c"></u></style>
        <s id="k3y6c"></s>
        <mark id="k3y6c"></mark>
          
          

          <mark id="k3y6c"></mark>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 看時(shí)序圖寫(xiě)I2C驅(qū)動(dòng),教你如何自己手?jǐn)]非標(biāo)I2C驅(qū)動(dòng)函數(shù)

          看時(shí)序圖寫(xiě)I2C驅(qū)動(dòng),教你如何自己手?jǐn)]非標(biāo)I2C驅(qū)動(dòng)函數(shù)

          作者: 時(shí)間:2025-08-18 來(lái)源:硬件筆記本 收藏

          很多人不知道怎么看著圖寫(xiě)程序,下面結(jié)合一個(gè)非標(biāo)準(zhǔn)的器件,教大家如何寫(xiě)一個(gè)高效的IO模擬


          觀(guān)察該,具備的開(kāi)始信號(hào),I2C的結(jié)束信號(hào),I2C的應(yīng)答、非應(yīng)答、響應(yīng)應(yīng)答,以及寫(xiě)字節(jié)和讀字節(jié)的基本操作時(shí)序。


          下面,我們一步一步分析。


          1、I2C開(kāi)始信號(hào)


          觀(guān)察時(shí)序圖,在SCLK高電平的狀態(tài)下,在SDIO產(chǎn)生一個(gè)下降沿是為開(kāi)始信號(hào)。


          void I2C_Start()
          {  //設(shè)置I2C使用的兩個(gè)引腳為輸出模式
            pinMode(SCLK_PIN, OUTPUT);  pinMode(SDIO_PIN, OUTPUT);  //在SCL為高電平的時(shí)候讓SDA產(chǎn)生一個(gè)下降沿是為開(kāi)始信號(hào)
            digitalWrite(SDIO_PIN, 1);  digitalWrite(SCLK_PIN, 1);  digitalWrite(SDIO_PIN, 0);
          }


          上述代碼即先將兩個(gè)引腳設(shè)置為輸出模式,然后在SCLK為高電平的時(shí)候在SDIO引腳輸出一個(gè)下降沿。


          2、I2C停止信號(hào)


          觀(guān)察時(shí)序圖,在SCLK為高電平的時(shí)候在SDIO引腳產(chǎn)生一個(gè)上升沿是為停止信號(hào)。


          void I2C_Stop()
          {  pinMode(SDIO_PIN, OUTPUT);  //在SCL為高電平的時(shí)候讓SDA產(chǎn)生一個(gè)上升沿是為停止信號(hào)
            digitalWrite(SDIO_PIN, 0);  digitalWrite(SCLK_PIN, 1);  digitalWrite(SDIO_PIN, 1);
          }


          這里采用的是Arduino編寫(xiě)的IO基本操作,你可以替換成任意單片機(jī)的IO操作。


          由于整個(gè)過(guò)程SCLK引腳一直是輸出狀態(tài),所以?xún)H在開(kāi)始信號(hào)中對(duì)SCLK初始化為輸出模式,而過(guò)程中可能會(huì)修改SDIO的輸入輸出模式,所以其他的函數(shù)開(kāi)頭都要根據(jù)情況對(duì)SDIO引腳的模式進(jìn)行設(shè)置。


          通過(guò)三行代碼實(shí)現(xiàn)在SCLK為高電平的時(shí)候在SDIO產(chǎn)生一個(gè)上升沿,實(shí)現(xiàn)停止信號(hào)。


          3、寫(xiě)字節(jié)操作


          接下來(lái),按照時(shí)序的順序編寫(xiě)方便認(rèn)讀


          I2C的讀寫(xiě)字節(jié)是這么定義的:當(dāng)時(shí)鐘線(xiàn)為低電平的時(shí)候,允許修改數(shù)據(jù)線(xiàn)的電平狀態(tài),在時(shí)鐘線(xiàn)為高電平的時(shí)候讀取數(shù)據(jù)線(xiàn)的狀態(tài)。


          因?yàn)槭菍?xiě)操作,因此我們要先將時(shí)鐘線(xiàn)SCLK拉低,再修改SDIO的值,然后拉高時(shí)鐘。拉高后,從機(jī)就會(huì)從總線(xiàn)上讀取SDIO的狀態(tài),接著一位一位的這么發(fā)送。


          void I2C_Write(uint8_t dat){
            pinMode(SDIO_PIN, OUTPUT);  //拉低時(shí)鐘線(xiàn)后可修改數(shù)據(jù)線(xiàn)的狀態(tài)
            digitalWrite(SCLK_PIN, 0); 
            for(int i=0;i<8;i++)
            {
              digitalWrite(SDIO_PIN, (bool)(dat&0x80)); 
              digitalWrite(SCLK_PIN, 1);//在高電平時(shí)候送出數(shù)據(jù)
              dat=dat<<1;
              digitalWrite(SCLK_PIN, 0);//拉低準(zhǔn)備下一個(gè)位的數(shù)據(jù)發(fā)送
            }
          }


          上述代碼正描述了這一情況:為了保證最后是低電平,這里將SCLK的第一次拉低放到循環(huán)外面,這樣可以用最少的執(zhí)行次數(shù)完成一個(gè)字節(jié)的寫(xiě)任務(wù);同時(shí),結(jié)束完一個(gè)字節(jié)寫(xiě)入后時(shí)鐘線(xiàn)是低電平狀態(tài)(時(shí)序圖中寫(xiě)入的第一個(gè)字節(jié)為DeviceID,第二個(gè)字節(jié)為寄存器地址+讀寫(xiě)位)。


          寫(xiě)完一個(gè)字節(jié)后,從機(jī)會(huì)對(duì)寫(xiě)入事件進(jìn)行應(yīng)答,這個(gè)時(shí)候主機(jī)可以從總線(xiàn)上讀取應(yīng)答信號(hào)。


          4、讀取從機(jī)應(yīng)答引號(hào)


          應(yīng)答信號(hào)在寫(xiě)完一個(gè)字節(jié)的低電平后由從機(jī)送出,在時(shí)鐘為高電平的時(shí)候可以讀取出來(lái),我們注意到寫(xiě)字節(jié)操作后時(shí)鐘線(xiàn)已經(jīng)是低電平了,因此這個(gè)時(shí)候


          只要拉高時(shí)鐘線(xiàn),接下來(lái)就可以讀取應(yīng)答信號(hào),讀取應(yīng)答信號(hào)根據(jù)時(shí)序圖應(yīng)該拉低時(shí)鐘準(zhǔn)備下一個(gè)字節(jié)的寫(xiě)入。


          bool I2C_RACK(){  bool ack;
            pinMode(SDIO_PIN, INPUT);
          
          
            digitalWrite(SCLK_PIN, 1);//接收應(yīng)答信號(hào),當(dāng)時(shí)鐘拉高時(shí)候,從機(jī)送出應(yīng)答信號(hào)
            ack = digitalRead(SDIO_PIN);
            digitalWrite(SCLK_PIN, 0);//讀取完應(yīng)答信號(hào)后拉低時(shí)鐘。
            return ack;
          }


          如上代碼所示,即為接收從機(jī)應(yīng)答,拉高時(shí)鐘,讀取應(yīng)答,再拉低,返回應(yīng)答。如果從機(jī)應(yīng)答了,這里會(huì)讀取到一個(gè)低電平。


          后面就是再寫(xiě)入一個(gè)寄存器+讀寫(xiě)位的地址,參靠上面的寫(xiě)入操作。


          寫(xiě)入寄存器地址后,緊跟著又一個(gè)接收從機(jī)應(yīng)答信號(hào),然后從機(jī)就會(huì)送出數(shù)據(jù),送出的數(shù)據(jù)分高字節(jié)和低字節(jié),高低字節(jié)間要有一個(gè)主機(jī)發(fā)送給從機(jī)的應(yīng)答信號(hào),這樣從機(jī)就知道主機(jī)收到了數(shù)據(jù),就會(huì)送出后面的低字節(jié)數(shù)據(jù)。


          5、讀字節(jié)操作


          注意,前面說(shuō)過(guò),讀寫(xiě)都是總線(xiàn)在時(shí)鐘低電平時(shí)候修改數(shù)據(jù)線(xiàn),在高電平送出。


          因此,主機(jī)讀取從機(jī)送來(lái)的數(shù)據(jù)仍然是在高電平時(shí)候讀取。


          uint8_t I2C_Read()
          {  uint8_t dat=0;
            pinMode(SDIO_PIN, INPUT);  for(int i=0;i<8;i++)
            {
              digitalWrite(SCLK_PIN, 1);//讀取數(shù)據(jù)時(shí)候是在時(shí)鐘的高電平狀態(tài)讀取
              dat=dat<<1;    if(digitalRead(SDIO_PIN))
              {
                dat=dat|1;
              }
              digitalWrite(SCLK_PIN, 0);//拉低時(shí)鐘線(xiàn)準(zhǔn)備下一個(gè)位的讀取
            }  return dat;
          }


          操作過(guò)程是將SDIO數(shù)據(jù)線(xiàn)的IO設(shè)置為輸入模式,準(zhǔn)備讀取,然后拉高時(shí)鐘,讀取數(shù)據(jù),移位,拉低循環(huán)讀取8位數(shù)據(jù)。


          注意,操作完一個(gè)字節(jié)讀取任務(wù)后,時(shí)鐘線(xiàn)還是低電平。


          讀取完一個(gè)字節(jié)后,主機(jī)要給從機(jī)發(fā)送一個(gè)應(yīng)答信號(hào),這樣從機(jī)會(huì)接著發(fā)低字節(jié)數(shù)據(jù)。


          6、主機(jī)發(fā)送應(yīng)答信號(hào)給從機(jī)


          void I2C_ACK()
          {pinMode(SDIO_PIN, OUTPUT);digitalWrite(SDIO_PIN, 0);//給從機(jī)發(fā)送應(yīng)答信號(hào),即拉低數(shù)據(jù)線(xiàn),然后拉高時(shí)鐘讓從機(jī)讀取該應(yīng)答digitalWrite(SCLK_PIN, 1);digitalWrite(SCLK_PIN, 0);//執(zhí)行完應(yīng)答后拉低時(shí)鐘線(xiàn),準(zhǔn)備下一步動(dòng)作。}


          拉低數(shù)據(jù)線(xiàn),然后在高電平的時(shí)候讓從機(jī)去讀取,之后拉低時(shí)鐘線(xiàn)準(zhǔn)備下一步接收動(dòng)作。


          當(dāng)再接收一個(gè)字節(jié)后,就讀取完成了,這個(gè)時(shí)候就是產(chǎn)生一個(gè)非應(yīng)答信號(hào),然后發(fā)給總線(xiàn)結(jié)束信號(hào),告訴從機(jī)一個(gè)讀寫(xiě)周期結(jié)束了。


          7、主機(jī)非應(yīng)答信號(hào)


          什么是非應(yīng)答信號(hào)呢?


          就是接收完了數(shù)據(jù),釋放數(shù)據(jù)線(xiàn),不去拉低數(shù)據(jù)線(xiàn)。


          void I2C_NACK()
          {  //非應(yīng)答信號(hào):即主機(jī)不再對(duì)從機(jī)進(jìn)行應(yīng)答,主機(jī)釋放數(shù)據(jù)線(xiàn),即拉高數(shù)據(jù)線(xiàn),然后給時(shí)鐘一個(gè)周期信號(hào)(拉高再拉低)
            pinMode(SDIO_PIN, OUTPUT);  digitalWrite(SDIO_PIN, 1);  digitalWrite(SCLK_PIN, 1);  digitalWrite(SCLK_PIN, 0);
          }


          將SDIO引腳設(shè)置為輸出,拉高數(shù)據(jù)線(xiàn),即為釋放數(shù)據(jù)線(xiàn),然后拉高拉低時(shí)鐘,即在時(shí)鐘線(xiàn)產(chǎn)生一個(gè)時(shí)鐘周期信號(hào)。


          然后發(fā)送結(jié)束信號(hào)。結(jié)束信號(hào)在開(kāi)頭已經(jīng)講明,即在時(shí)鐘線(xiàn)為高電平的狀態(tài)下,在數(shù)據(jù)線(xiàn)產(chǎn)生一個(gè)上升沿。


          觀(guān)察以上代碼沒(méi)一個(gè)多余重復(fù)的操作動(dòng)作,即完美的視線(xiàn)了時(shí)序圖上的所有操作。


          接下來(lái)就是利用上述的I2C成分進(jìn)行對(duì)寄存器的讀寫(xiě)操作了。


          8、讀寄存器


          由于圖中設(shè)備的DeviceID 為0x80,即直接寫(xiě)進(jìn)來(lái),從機(jī)判斷是讀還是寫(xiě)的字節(jié)在寄存器地址。


          因此,將寄存器的地址左移一位,在末尾補(bǔ)上是讀(1)還是寫(xiě)(0)。


          uint16_t read_reg(uint8_t reg){
            uint16_t dat=0;
            reg=(reg<<1)|1;
            I2C_Start();
            I2C_Write(0x80);
            I2C_RACK();
            I2C_Write(reg);
            I2C_RACK();
            dat=I2C_Read();
            dat=dat<<8;
            I2C_ACK();
            dat=dat|I2C_Read();
            I2C_NACK(); 
            I2C_Stop();
            return dat;}


          9、寫(xiě)寄存器操作


          void write_reg(uint8_t reg, uint16_t dat){
            reg=(reg<<1);
            I2C_Start();
            I2C_Write(0x80);
            I2C_RACK();
            I2C_Write(reg);
            I2C_RACK();
            I2C_Write(dat>>8);
            I2C_RACK();
            I2C_Write(dat&0xFF);
            I2C_NACK();
            I2C_Stop();
          }


          最后,對(duì)寄存器讀寫(xiě)函數(shù)測(cè)試。

          void setup() 
          {  Serial.begin(115200);  Serial.println("Hello I2C");  write_reg(0x02,0x2250);  Serial.println(read_reg(0x02),HEX);  write_reg(0x02,0x2281);  Serial.println(read_reg(0x02),HEX);
          }void loop() 
          {
          
          
          }



          讀取的數(shù)值與寫(xiě)入的是一樣的。


          最后曬出完整的測(cè)試代碼:


          #define SCLK_PIN 8#define SDIO_PIN 9void I2C_Start(){  //設(shè)置I2C使用的兩個(gè)引腳為輸出模式
            pinMode(SCLK_PIN, OUTPUT);
            pinMode(SDIO_PIN, OUTPUT);  //在SCL為高電平的時(shí)候讓SDA產(chǎn)生一個(gè)下降沿是為開(kāi)始信號(hào)
            digitalWrite(SDIO_PIN, 1);
            digitalWrite(SCLK_PIN, 1);
            digitalWrite(SDIO_PIN, 0);
          }void I2C_Stop(){
            pinMode(SDIO_PIN, OUTPUT);  //在SCL為高電平的時(shí)候讓SDA產(chǎn)生一個(gè)上升沿是為停止信號(hào)
            digitalWrite(SDIO_PIN, 0);
            digitalWrite(SCLK_PIN, 1);
            digitalWrite(SDIO_PIN, 1);
          }void I2C_Write(uint8_t dat){
            pinMode(SDIO_PIN, OUTPUT);  //拉低時(shí)鐘線(xiàn)后可修改數(shù)據(jù)線(xiàn)的狀態(tài)
            digitalWrite(SCLK_PIN, 0); 
            for(int i=0;i<8;i++)
            {
              digitalWrite(SDIO_PIN, (bool)(dat&0x80)); 
              digitalWrite(SCLK_PIN, 1);//在高電平時(shí)候送出數(shù)據(jù)
              dat=dat<<1;
              digitalWrite(SCLK_PIN, 0);//拉低準(zhǔn)備下一個(gè)位的數(shù)據(jù)發(fā)送
            }
          }uint8_t I2C_Read()
          {  uint8_t dat=0;
            pinMode(SDIO_PIN, INPUT);  for(int i=0;i<8;i++)
            {
              digitalWrite(SCLK_PIN, 1);//讀取數(shù)據(jù)時(shí)候是在時(shí)鐘的高電平狀態(tài)讀取
              dat=dat<<1;    if(digitalRead(SDIO_PIN))
              {
                dat=dat|1;
              }
              digitalWrite(SCLK_PIN, 0);//拉低時(shí)鐘線(xiàn)準(zhǔn)備下一個(gè)位的讀取
            }  return dat;
          }bool I2C_RACK(){  bool ack;
            pinMode(SDIO_PIN, INPUT);
          
          
            digitalWrite(SCLK_PIN, 1);//接收應(yīng)答信號(hào),當(dāng)時(shí)鐘拉高時(shí)候,從機(jī)送出應(yīng)答信號(hào)
            ack = digitalRead(SDIO_PIN);
            digitalWrite(SCLK_PIN, 0);//讀取完應(yīng)答信號(hào)后拉低時(shí)鐘。
            return ack;
          }void I2C_ACK(){
            pinMode(SDIO_PIN, OUTPUT);
            digitalWrite(SDIO_PIN, 0);//給從機(jī)發(fā)送應(yīng)答信號(hào),即拉低數(shù)據(jù)線(xiàn),然后拉高時(shí)鐘讓從機(jī)讀取該應(yīng)答
            digitalWrite(SCLK_PIN, 1);
            digitalWrite(SCLK_PIN, 0);//執(zhí)行完應(yīng)答后拉低時(shí)鐘線(xiàn),準(zhǔn)備下一步動(dòng)作。}void I2C_NACK(){  //非應(yīng)答信號(hào):即主機(jī)不再對(duì)從機(jī)進(jìn)行應(yīng)答,主機(jī)釋放數(shù)據(jù)線(xiàn),即拉高數(shù)據(jù)線(xiàn),然后給時(shí)鐘一個(gè)周期信號(hào)(拉高再拉低)
            pinMode(SDIO_PIN, OUTPUT);
            digitalWrite(SDIO_PIN, 1);
            digitalWrite(SCLK_PIN, 1);
            digitalWrite(SCLK_PIN, 0);
          }uint16_t read_reg(uint8_t reg)
          {  uint16_t dat=0;
            reg=(reg<<1)|1;
            I2C_Start();
            I2C_Write(0x80);
            I2C_RACK();
            I2C_Write(reg);
            I2C_RACK();
            dat=I2C_Read();
            dat=dat<<8;
            I2C_ACK();
            dat=dat|I2C_Read();
            I2C_NACK(); 
            I2C_Stop();  return dat;
          }void write_reg(uint8_t reg, uint16_t dat){
            reg=(reg<<1);
            I2C_Start();
            I2C_Write(0x80);
            I2C_RACK();
            I2C_Write(reg);
            I2C_RACK();
            I2C_Write(dat>>8);
            I2C_RACK();
            I2C_Write(dat&0xFF);
            I2C_NACK();
            I2C_Stop();
          }void setup() {
            Serial.begin(115200);
            Serial.println("Hello I2C");
            write_reg(0x02,0x2250);
            Serial.println(read_reg(0x02),HEX);
            write_reg(0x02,0x2281);
            Serial.println(read_reg(0x02),HEX);
          }void loop() {
          
          
          }


          看完這篇文章,你學(xué)會(huì)純手工擼IO模擬I2C時(shí)序的代碼了嗎?



          關(guān)鍵詞: I2C 時(shí)序

          評(píng)論


          相關(guān)推薦

          技術(shù)專(zhuān)區(qū)

          關(guān)閉