官术网_书友最值得收藏!

4.2 STM32單片機輸入端口的應(yīng)用

在第2章,你就已經(jīng)知道了STM32系列單片機有5個16位的并行I/O口:PA、PB、PC、PD和PE。這5個端口,既可以作為輸入,也可以作為輸出,既可按16位處理,也可按位方式使用。實際上,在單片機復(fù)位期間和剛復(fù)位后,復(fù)用功能未開啟,I/O端口被配置成浮空輸入模式。所有端口都有外部中斷能力。為了使用外部中斷線,端口必須配置成輸入模式。

作為輸入,如果I/O腳上的電壓為高電平(5V或3.3V),則其相對應(yīng)的I/O口寄存器中的相應(yīng)位存儲1;如果電壓為低電平(0V),則存儲0。

布置恰當?shù)碾娐?,你可以讓胡須達到上述效果:當胡須沒有被碰到時,使I/O腳上的電壓為高電平(5V或3.3V);當胡須被碰到時,則使I/O腳上電壓為低電平(0V)。然后,單片機就可以讀入相應(yīng)數(shù)據(jù),進行分析、處理,控制機器人的運動。安裝好觸須的機器人小車全貌如圖4.2所示。

圖4.2 安裝好觸須的機器人小車全貌

任務(wù)二 安裝并測試機器人的觸覺——胡須

讓機器人通過觸覺胡須進行導(dǎo)航,首先必須安裝并測試胡須。圖4.3所示是所需的硬件元件清單,包括:

圖4.3 胡須硬件

(1)金屬絲2根;

(2)平頭M3×22盤頭螺釘2個;

(3)13mm圓形立柱2個;

(4)M3尼龍墊圈2個;

(5)3-pin公-公接頭2個;

(6)2個220?電阻(色環(huán):紅—紅—黑—黑);

(7)2個10k?電阻(色環(huán):棕—黑—黑—紅)。

參考圖4.4,安裝胡須

圖4.4 安裝機器人胡須

螺釘依次穿過M3尼龍墊圈、13mm圓形立柱;

螺釘穿過主板上的圓孔之后,擰進主板下面的支架中,但不要擰緊;

把須狀金屬絲的其中一個鉤在尼龍墊圈之上,另一個鉤在尼龍墊圈之下,調(diào)整它們的位置使它們橫向交叉但又不接觸;擰緊螺釘?shù)街Ъ苌希?/p>

參考接線圖4.5,搭建胡須電路。注意:右邊胡須狀態(tài)信息輸入是通過PE口的第1腳完成的,而左邊胡須狀態(tài)信息輸入是通過PE口的第0腳完成的;

圖4.5 胡須電路示意圖

確定兩條胡須比較靠近,但又不接觸面包板上的3-pin頭,推薦保持3mm的距離;

圖4.6所示是實際的參考接線圖。安裝好觸覺胡須的教學(xué)開發(fā)板如圖4.7所示。

圖4.6 胡須接線圖

圖4.7 安裝好觸須的教學(xué)開發(fā)板

測試胡須

觀察一下圖4.5所示的胡須電路示意圖,顯然每條胡須都是一個機械式的、接地常開的開關(guān)(類似按鍵)。胡須接地(GND)是因為教學(xué)板外圍的鍍金孔都連接到GND。金屬支架和螺絲釘提供電氣連接給胡須。

通過編程讓單片機探測什么時候胡須被觸動。由圖4.5可知,連接到每個胡須電路的I/O引腳監(jiān)視著10kΩ上拉電阻上的電壓變化。當胡須沒有被觸動,連接胡須的I/O引腳的電壓是高電平;當胡須被觸動時,I/O短接到地,所以I/O引腳的電壓是低電平。

上拉電阻

上拉電阻就是與電源相連,并起到拉高電平作用的電阻。此電阻還起到限流的作用,如圖4.5中的10kΩ電阻即為上拉電阻。與之對應(yīng)的還有“下拉電阻”,它與“地(GND)”相連,可把電平拉至低位。

例程:TestWhiskers.c

      #include "stm32f10x_heads.h"
      #include "HelloRobot.h"
      int PE2state(void)//獲取PE2的狀態(tài)
      {
          return GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2);
      }
      int PE3state(void)//獲取PE3的狀態(tài)
      {
          return GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3);
      }
      int main(void)
      {
          BSP_Init();
          USART_Configuration();
          printf("Program Running!\r\n");
          while(1)
          {
            printf("右邊胡須的狀態(tài):%d ", PE2state());
            printf("左邊胡須的狀態(tài):%d\r\n",PE3state());
            delay_nms(150);
          }
      }

上面的例程是用來測試胡須的功能是否正常。首先,定義了兩個無參數(shù)有返回值子函數(shù)int PE2state(void)和int PE3state(void)來獲取左右兩個胡須的狀態(tài)。STM32單片機的5個端口PA、PB、PC、PD和PE是可以按位來操作的,從低到高依次為第0口、第1口、……、第15口。

在搞清楚整個程序的執(zhí)行原理后,按照下面的步驟實際執(zhí)行程序,對觸覺胡須進行測試。

● 連接好串口電纜,接通教學(xué)板和伺服電機的電源。

● 輸入、保存并運行程序TestWhiskers.c。

● 檢查圖4.5,弄清楚哪條胡須是左胡須,哪條是右胡須。

● 此時調(diào)試終端顯示:“右邊胡須的狀態(tài):1左邊胡須的狀態(tài):1”,如圖4.8所示。

圖4.8 左右胡須均未碰到

● 把右胡須按到3-pin轉(zhuǎn)接頭上,注意顯示為:“右邊胡須的狀態(tài):0左邊胡須的狀態(tài):1”,如圖4.9所示。

圖4.9 右胡須碰到

● 把左胡須按到3-pin轉(zhuǎn)接頭上,注意顯示為:“右邊胡須的狀態(tài):1左邊胡須的狀態(tài):0”,如圖4.10所示。

圖4.10 左胡須碰到

● 同時把兩個胡須按到各自的3-pin轉(zhuǎn)接頭上,顯示為:“右邊胡須的狀態(tài):0左邊胡須的狀態(tài):0”,如圖4.11所示。

圖4.11 左右胡須均碰到

● 如果兩個胡須都通過測試,你可以繼續(xù)下面的內(nèi)容;否則檢查程序或電路中存在的錯誤。

任務(wù)三 基于胡須的機器人觸覺導(dǎo)航

任務(wù)二中,你已經(jīng)通過編程檢測胡須是否被觸動。在本任務(wù)中將利用這些信息對機器人進行運動導(dǎo)航。在機器人行走過程中,如果有胡須被觸動,那就意味著碰到了什么。導(dǎo)航程序需要接受這些輸入信息,判斷它的意義,調(diào)用一系列使機器人倒退、旋轉(zhuǎn)朝不同方向行走的動作子函數(shù)以避開障礙物。

下面的程序讓機器人向前走直到碰到障礙物。在這種情況下,機器人用它的一根或者兩根胡須探測障礙物。一旦胡須探測到障礙物,調(diào)用第3 章中的導(dǎo)航程序和子程序使小車倒退或者旋轉(zhuǎn),然后再重新向前行走,直到遇到另一個障礙物。

賦值運算符“=”與關(guān)系運算符“==”

注意賦值運算符“=”與關(guān)系運算符“==”的區(qū)別:賦值運算符“=”用來給變量賦值;關(guān)系運算符“==”判斷兩個值是否是相等的關(guān)系。

邏輯與“&&”運算符的運算規(guī)則:

      A&&B        若A、B都為真,則A&&B為真。
      注意區(qū)分位操作符“&”和邏輯運算符“&&”。

例程:RoamingWithWhiskers.c

● 打開主板和伺服電機的電源;

● 輸入、保存并運行程序RoamingWithWhiskers.c;

● 嘗試讓機器人運動,當在其路線上遇到障礙物時,它將后退、旋轉(zhuǎn)并向另一個方向。

      #include "stm32f10x_heads.h"
      #include "HelloRobot.h"
      int PE2state(void)//獲取PE2的狀態(tài)
      {
          return GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2);
      }
      int PE3state(void)//獲取PE3的狀態(tài)
      {
          return GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3);
      }
      void Forward(void)
      {
            GPIO_SetBits(GPIOD, GPIO_Pin_10);
            delay_nus(1700);
          GPIO_ResetBits(GPIOD,GPIO_Pin_10);
            GPIO_SetBits(GPIOD, GPIO_Pin_9);
            delay_nus(1300);
            GPIO_ResetBits(GPIOD,GPIO_Pin_9);
            delay_nms(20);
      }
      void Left_Turn(void)
      {
        int i;
        for(i=1;i<=26;i++)
          {
            GPIO_SetBits(GPIOD, GPIO_Pin_10);
            delay_nus(1300);
          GPIO_ResetBits(GPIOD,GPIO_Pin_10);
            GPIO_SetBits(GPIOD, GPIO_Pin_9);
            delay_nus(1300);
            GPIO_ResetBits(GPIOD,GPIO_Pin_9);
            delay_nms(20);
          }
      }
      void Right_Turn(void)
      {
        int i;
        for(i=1;i<=26;i++)
          {
            GPIO_SetBits(GPIOD, GPIO_Pin_10);
            delay_nus(1700);
          GPIO_ResetBits(GPIOD,GPIO_Pin_10);
            GPIO_SetBits(GPIOD, GPIO_Pin_9);
            delay_nus(1700);
            GPIO_ResetBits(GPIOD,GPIO_Pin_9);
            delay_nms(20);
          }
      }
      void Backward(void)
      {
        int i;
        for(i=1;i<=65;i++)
          {
            GPIO_SetBits(GPIOD, GPIO_Pin_10);
            delay_nus(1300);
          GPIO_ResetBits(GPIOD,GPIO_Pin_10);
            GPIO_SetBits(GPIOD, GPIO_Pin_9);
            delay_nus(1700);
            GPIO_ResetBits(GPIOD,GPIO_Pin_9);
            delay_nms(20);
          }
      }
      int main(void)
      {
          BSP_Init();
          USART_Configuration();
          printf("Program Running!\n");
          while(1)
          {
            if((PE2state()==0)&&(PE3state()==0))   //兩胡須同時碰到
            {
                  Backward();         //向后
                  Left_Turn();        //向左
                  Left_Turn();        //向左
            }
            else if(PE2state()==0)    //右胡須碰到
            {
                  Backward();         //向后
                  Left_Turn();        //向左
            }
            else if(PE3state()==0)    //左胡須碰到
            {
                  Backward();         //向后
                  Right_Turn();       //向右
            }
            else                      //胡須沒有碰到
                  Forward();          //向前
          }
      }

注意:函數(shù)Forward()有一個變動。它只發(fā)送一個脈沖,然后返回。這點相當重要,因為機器人可以在向前運動中的每兩個脈沖之間檢查胡須的狀態(tài)。意味著,機器人在向前行走的過程中,每秒檢查觸須狀態(tài)大概43次(1000ms/23ms≈=43)。

因為每個全速前進的脈沖都使得機器人前進大約半厘米。只發(fā)送一個脈沖,然后回去檢查胡須的狀態(tài)是一個好主意。每次程序從Forward()返回后,程序再次從while循環(huán)的開始處執(zhí)行,此時if…else語句會再次檢查胡須的狀態(tài)。

任務(wù)四 機器人進入死區(qū)后的人工智能決策

你或許已注意到機器人卡在墻角里的情況。當機器人進入墻角時,左胡須觸墻,于是它右轉(zhuǎn),向前行走,右胡須觸墻,于是左轉(zhuǎn)前進,又碰到左墻,再次碰到右墻……如果不是你把它從墻角拿出來,它就會一直困在墻角里而出不來。

編程逃離墻角死區(qū)

你可以修改RoamingWithWhiskers.c來讓機器人碰到上述問題時逃離死區(qū)。技巧是記下胡須交替觸動的總次數(shù)。技巧的關(guān)鍵是程序必須記住每個胡須的前一次觸動狀態(tài),并和當前觸動狀態(tài)對比。如果狀態(tài)相反,就在交替總數(shù)上加1。如果這個交替總數(shù)超過了程序中預(yù)先給定的閥值,表示這是個墻角(死區(qū)),那么就該做一個“U”型轉(zhuǎn)彎,并且把胡須交替計數(shù)器復(fù)位。

這個技巧的編程實現(xiàn)依賴于if…else嵌套語句。換句話說,程序檢查一種條件,如果該條件成立(條件為真),則再檢查包含于這個條件之內(nèi)的另一個條件。下面是用偽代碼說明嵌套語句的用法。

      IF (condition1)
      {
      commands for condition1
      IF(condition2)
      {
      commands for both condition2 and condition1
      }
      ELSE
      {
      commands for condition1 but not condition2
      }
      }
      ELSE
      {
      commands for not condition1
      }

偽代碼通常用來描述不依賴于計算機語言的算法。實際上在前面幾章的任務(wù)和小結(jié)中,已經(jīng)多次提醒和暗示你,無論是哪種計算機語言,都必須能夠描述人類知識的邏輯結(jié)構(gòu)。而人類知識的邏輯結(jié)構(gòu)是統(tǒng)一的,如條件判斷就是人類知識最核心的邏輯之一。因此,各種計算機語言都有語法和關(guān)鍵詞來實現(xiàn)條件判別。因此,在寫條件判斷算法時,經(jīng)常用一種用于描述人類知識結(jié)構(gòu)邏輯的偽代碼來描述在計算機中如何實現(xiàn)這些邏輯算法,以使算法具有通用性。有了偽代碼,用具體的語言來實現(xiàn)算法就很簡單了。

下面的例程,用于探測連續(xù)的、交替出現(xiàn)的胡須觸動過程。這個程序使機器人在第四次或第五次交替探測到墻角后,完成一個“U”型的拐彎,次數(shù)依賴于哪一個胡須先被觸動。

● 輸入、保存并運行程序EscapingCorners.c;

● 在機器人行走時,輪流觸動它的胡須,測試該程序。

例程:EscapingCorners.c

      #include "stm32f10x_heads.h"
      #include "HelloRobot.h"
      int PE2state(void)//獲取PE2的狀態(tài)
      {
          return GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2);
      }
      int PE3state(void)//獲取PE3的狀態(tài)
      {
          return GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3);
      }
      void Forward(void)
      {
      …  //略,同前
      }
      void Left_Turn(void)
      {
      …  //略,同前
      }
      void Right_Turn(void)
      {
      …  //略,同前
      }
      void Backward(void)
      {
      …  //略,同前
      }
      int main(void)
      {
          int counter=1;  //胡須碰撞總次數(shù)
          int old2=1;     //右胡須舊狀態(tài)
          int old3=0;     //左胡須舊狀態(tài)
          BSP_Init();
          USART_Configuration();
          printf("Program Running!\n");
          while(1)
          {
            if(PE3state()!=PE2state())
            {
                  if((old2!=PE2state())&&(old3!=PE3state()))
                  {
                         counter=counter+1;
                         old2=PE2state();
                         old3=PE3state();
                         if(counter>4)
                         {
                          counter=1;
                          Backward();//向后
                          Left_Turn();//向左
                          Left_Turn();//向左
                         }
                  }
                  else
                         counter=1;
            }
            if((PE3state()==0)&&(PE2state()==0))
            {
                  Backward();  //向后
                  Left_Turn(); //向左
                  Left_Turn(); //向左
            }
            else if(PE2state()==0)
            {
                  Backward();  //向后
                  Left_Turn(); //向左
            }
            else if(PE3state()==0)
            {
                  Backward();  //向后
                  Right_Turn();//向右
            }
            else
                  Forward();   //向前
          }
      }

EscapingCorners.c是如何工作的?

由于該程序是經(jīng)RoamingWithWhiskers.c修改而來,下面只討論與探測和逃離墻角相關(guān)的新特征。

      int counter=1;
      int old2=1;
      int old3=0;

這三個變量用于探測墻角。int型變量counter用來存儲交替探測的次數(shù)。例程中,設(shè)定的交替探測的最大值為4。int型變量old2、old3存儲胡須舊的狀態(tài)值。

程序賦counter初值為1,當機器人卡在墻角此值累計到4時,counter復(fù)位為1。old2和old3必須賦值以至于看起來好像兩根胡須的其中一根在程序開始之前被觸動了。這些工作之所以必須做,是因為探測墻角的程序總是對比交替觸動的部分,或者PE2state()==0,或者PE3state()==0。與之對應(yīng),old2和old3的值也相互不同。

現(xiàn)在看探測連續(xù)而交替觸動墻角的部分。

首先要檢查的是,是否有且只有一個胡須被觸動。簡單的方法就是詢問“是否PE2state()不等于PE3state()”。其具體判斷語句如下:

      if(PE3state()!=PE3state())

假如真有胡須被觸動,接下來要做的事情就是檢查當前狀態(tài)是否確實與上次不同。換句話說,是old2不等于PE2state()和old3不等于PE3state()嗎?如果是,就在胡須觸動計數(shù)器上加1,同時記下當前的狀態(tài),設(shè)置old2等于當前的PE2state(),old3等于當前的PE3state()。

            if((PE2state()==0)&&(PE3state()==0))
            {
                  Backward();//向后
                  Left_Turn();//向左
                  Left_Turn();//向左
            }

如果發(fā)現(xiàn)胡須連續(xù)四次被觸動,那么計數(shù)值置1,并且進行“U”型拐彎。

      if(counter>4)
      {
      counter=1;
          Backward();
          Left_Turn();
          Left_Turn();
      }

緊接的else語句是機器人沒有陷入墻角情況,故需要將計數(shù)器值置1。之后的程序和RoamingWithWhiskers.c中的一樣。

該你了

● 嘗試增加變量counter的數(shù)值為5和6,注意結(jié)果;

● 嘗試減小變量counter的數(shù)值,觀察小車在正常行走過程中是否有任何不同。

主站蜘蛛池模板: 三台县| 江达县| 琼海市| 沅陵县| 康平县| 望奎县| 平利县| 平潭县| 尼玛县| 泰兴市| 鹤山市| 永善县| 山东省| 谢通门县| 青州市| 新沂市| 祥云县| 清原| 那曲县| 中江县| 上饶市| 黑龙江省| 福建省| 惠安县| 西峡县| 平凉市| 新乡县| 南城县| 德化县| 八宿县| 县级市| 宁武县| 阆中市| 霍林郭勒市| 射阳县| 于都县| 聂拉木县| 武定县| 静宁县| 湟源县| 吉首市|