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

2.4 STM32單片機I/O端口的應用

任務(wù)五 機器人伺服電機控制信號

控制伺服電機轉(zhuǎn)動速度的脈沖信號如圖2.10、圖2.11和圖2.12所示。

圖2.10 電機轉(zhuǎn)速為零的控制信號時序圖

圖2.11 1.3ms的控制脈沖序列使電機順時針全速旋轉(zhuǎn)

圖2.12 1.7ms的連續(xù)脈沖序列使電機逆時針全速旋轉(zhuǎn)

圖2.10所示是高電平持續(xù)1.5ms低電平持續(xù)20ms,然后不斷重復地控制脈沖序列。該脈沖序列發(fā)給經(jīng)過零點標定后的伺服電機,伺服電機不會旋轉(zhuǎn)。如果此時你的電機旋轉(zhuǎn),表明電機需要標定。此時,你可調(diào)節(jié)伺服電機的可調(diào)電阻使電機停止旋轉(zhuǎn)。控制電機運動轉(zhuǎn)速的是高電平持續(xù)的時間,當高電平持續(xù)時間為1.3ms時,電機順時針全速旋轉(zhuǎn),當高電平持續(xù)時間1.7ms時,電機逆時針速旋轉(zhuǎn)。下面你將看到如何給STM32單片機微控制器編程使PD端口的第10腳(PD10)來發(fā)出伺服電機的控制信號。

在進行下面的實驗之前,你必須首先確認一下機器人兩個伺服電機的控制線是否已經(jīng)正確地連接到了STM32單片機教學開發(fā)板的兩個專用電機控制接口上。按照圖2.13所示的電機連接原理圖和實際接線圖進行檢查。“黑線”表示地線,“紅線”表示電源線,“白線”表示信號線。PD9用來控制左邊的伺服電機,而PD10引腳則用來控制右邊的伺服電機。

圖2.13 伺服電機與教學開發(fā)板的連接原理圖(左)和實際接線圖(右)

顯然這里對微控制器編程發(fā)給伺服電機的高、低電平信號必須具備更精確的時間。因為單片機只有整數(shù),沒有小數(shù),所以要生成伺服電機的控制信號,要求具有比delay_nms()函數(shù)的時間更精確的函數(shù),這就需要用另一個延時函數(shù)delay_nus()。前面已經(jīng)介紹過,這個函數(shù)可以實現(xiàn)更小的延時,它的延時單位是微秒,即千分之一毫秒,參數(shù)n為延時微秒數(shù)。

看看下面的代碼片斷:

      while (1)
      {
          GPIO_SetBits(GPIOD,GPIO_Pin_10);   //PD10輸出高電平
          delay_nus(1500);                   //延時1500μs
          GPIO_ResetBits(GPIOD,GPIO_Pin_10); //PD10輸出低電平
          delay_nus(20000);                  //延時20ms
      }

如果用這個代碼段代替例程Led_Blink.c中相應程序片斷,它是不是就會輸出圖2.6所示的脈沖信號?肯定是!如果你手邊有個示波器,可以用示波器觀察PD10腳輸出的波形是不是如圖2.6所示。此時,連接到該腳的機器人輪子是不是靜止不動。如果它在慢慢轉(zhuǎn)動,就說明你的機器人伺服電機可能沒有經(jīng)過調(diào)整。

同樣,用下面的程序片斷代替例程Led_Blink.c中相應程序片斷,編譯、連接下載執(zhí)行代碼,觀察連接到PD10腳的機器人輪子是不是順時針全速旋轉(zhuǎn)。

      while(1)
      {
          GPIO_SetBits(GPIOD,GPIO_Pin_10);   //PD10輸出高電平
          delay_nus(1300);                   //延時1500μs
          GPIO_ResetBits(GPIOD,GPIO_Pin_10); //PD10輸出低電平
          delay_nus(20000);                  //延時20ms
      }

用下面的程序片斷代替例程Led_Blink.c中相應程序片斷,編譯、連接下載執(zhí)行代碼,觀察連接到PD10腳的機器人輪子是不是逆時針全速旋轉(zhuǎn)。

      while (1)
      {
          GPIO_SetBits(GPIOD,GPIO_Pin_10);   //PD10輸出高電平
          delay_nus(1700);                   //延時1500μs
          GPIO_ResetBits(GPIOD,GPIO_Pin_10); //PD10輸出低電平
          delay_nus(20000);                  //延時20ms
      }

該你了——讓機器人的兩個輪子全速旋轉(zhuǎn)

剛才是讓連接到PD10腳的伺服電機輪子全速旋轉(zhuǎn),下面你自己可以修改程序讓連接到PD9機器人輪子全速旋轉(zhuǎn)。

當然,最后你需要修改程序,讓機器人的兩個輪子都能夠旋轉(zhuǎn)。讓機器人兩個輪子都順時鐘全速旋轉(zhuǎn)的程序參考下面的程序。

例程:BothServo.c

● 接通板上的電源,輸入、保存、下載并運行程序(整個過程請參考第1章);

● 觀察機器人小車的運動行為。

      #include "stm32f10x_heads.h"
      #include "HelloRobot.h"
      int main(void)
      {
        BSP_Init();
        USART_Configuration();
        printf("Program Running!\n");
        while (1)
        {
          GPIO_SetBits(GPIOD, GPIO_Pin_10);
          GPIO_SetBits(GPIOD, GPIO_Pin_9);
          delay_nus(1300);
          GPIO_ResetBits(GPIOD,GPIO_Pin_10);
          GPIO_ResetBits(GPIOD,GPIO_Pin_9);
          delay_nms(20);
        }
      }

注意:上述程序用到了兩個不同的延時函數(shù),效果與前面例子一樣。運行上述程序時,你是不是對機器人的運動行為感到驚訝!

任務(wù)六 計數(shù)并控制循環(huán)次數(shù)

任務(wù)五中已經(jīng)通過對STM32單片機編程實現(xiàn)了對機器人伺服電機的控制,為了讓微控制器不斷發(fā)出控制指令,你用到了以while(1)開頭的死循環(huán)(即永不結(jié)束的循環(huán))。不過在實際的機器人控制過程中,你會經(jīng)常要求機器人運動一段給定的距離或者一段固定的時間。這時就需要你能控制代碼執(zhí)行的次數(shù)。

最方便的控制一段代碼執(zhí)行次數(shù)的方法是利用for循環(huán),語法如下:

      for(表達式1;表達式2;表達式3) 語句

例如,下面是一個用整型變量myCounter來計數(shù)的for循環(huán)程序片斷。每執(zhí)行一次循環(huán),它會顯示myCounter的值。

      for(myCounter=1; myCounter<=10; myCounter++)
      {
      printf(“%d”,myCounter);
      delay_nms(500);
      }

該你了——不同的初始值和終值及計數(shù)步長

你可以修改表達式3來使myCounter以不同步長計數(shù),而不是按9,10,11,…來計,你可以讓它每次增加2(9,11,13,…)或增加5(10,15,20,…)或任何你想要的步進,遞增或遞減都可以。下面的例子是每次減3。

      for(myCounter=21; myCounter>=9; myCounter=myCounter-3)
      {
      printf(“%d\n”,myCounter);
      delay_nms(500);
      }

for循環(huán)控制電機的運行時間

到目前為止,你已經(jīng)理解了利用脈沖寬度調(diào)制(Pulse Width Modulation,PWM)來控制電機旋轉(zhuǎn)速度和方向的原理。控制電機速度和方向的方法是非常簡單的。控制電機運行的時間也非常簡單,那就是用for循環(huán)。下面是for循環(huán)的例子,它會使電機運行幾秒鐘。

      for(Counter=1;Counter<=100;i++)
      {
        GPIO_SetBits(GPIOD, GPIO_Pin_10);
        delay_nus(1700);
        GPIO_ResetBits(GPIOD,GPIO_Pin_10);
        delay_nms(20);
      }

讓我們來計算一下這個代碼能使電機轉(zhuǎn)動的確切的時間長度。每通過循環(huán)一次,delay_nus(1700)持續(xù)1.7 ms,delay_nms(20)持續(xù)20ms,其他語句的執(zhí)行時間很少,可忽略。那么for循環(huán)整體執(zhí)行一次的時間是:1.7ms+20ms=21.7ms,本循環(huán)執(zhí)行100次,即就是21.7ms乘以100,時間=100×21.7ms=100×0.0217s=2.17s。

例程:ControlServoRunTimes.c

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

● 驗證是否與PD10連接的電機逆時針旋轉(zhuǎn)2.17s,然后與PD9連接的電機旋轉(zhuǎn)4.34s。

      int main(void)
      {
        int Counter;
        BSP_Init();
        USART_Configuration();
        printf("Program Running!\n");
        for(Counter=1;Counter<=100;Counter++)
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_10);
            delay_nus(1700);
          GPIO_ResetBits(GPIOD,GPIO_Pin_10);
          delay_nms(20);
        }
        for(Counter=1;Counter<=200;Counter++)
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_9);
          delay_nus(1700);
            GPIO_ResetBits(GPIOD,GPIO_Pin_9);
          delay_nms(20);
        }
        while(1);
      }

假如你想讓兩個電機同時運行,給與PD10連接的電機發(fā)出1.7ms的脈寬,給與PD9連接的電機發(fā)出1.3ms的脈寬,現(xiàn)在每通過循環(huán)一次要用的時間是:

1.7ms——與PD10連接的電機

1.3ms——與PD8連接的電機

20 ms——中斷持續(xù)時間

---------- ------------------------------

一共是23 ms

假如你想讓電機運行3s,計算如下:脈沖數(shù)量=3 / 0.023= 130

現(xiàn)在,你可以將for循環(huán)中作如下修改,程序如下:

      for(counter=1;counter<=130;i++)
      {
      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);
      }

例程:BothServosThreeSeconds.c

下面是一個使電機向一個方向旋轉(zhuǎn)3s,然后反向旋轉(zhuǎn)的例子。

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

      int main(void)
      {
        int counter;
        BSP_Init();
        USART_Configuration();
        printf("Program Running!\n");
        for(counter=1;counter<=130;counter++)
        {
            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);
        }
        for(counter=1;counter<=130;counter++)
        {
            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);
          }
          while(1);
      }

驗證每個機器人是否沿一個方向運行3s然后反方向運行3s。你是否注意到當電機同時反向的時候,它們總是保持同步運行?這將有什么作用呢?

任務(wù)七 用你的計算機來控制機器人的運動

在工業(yè)自動化、測控等領(lǐng)域,經(jīng)常需要你的單片機與計算機通信。一方面,單片機需要讀取周邊傳感器的信息,并把數(shù)據(jù)傳給計算機;另一方面,計算機需要解釋和分析傳感器數(shù)據(jù),然后把分析結(jié)果或者決策發(fā)給單片機以執(zhí)行某種操作。

在第1章中你已經(jīng)知道STM32單片機可以通過串口向計算機發(fā)送信息,本章將使用串口和串口調(diào)試終端軟件,從計算機向單片機發(fā)送數(shù)據(jù)來控制機器人的運動。

在本任務(wù)中,你需要編程讓STM32單片機從調(diào)試窗口接收兩個數(shù)據(jù):發(fā)給伺服舵機的脈沖個數(shù)和脈沖寬度(以微秒為單位)。

例程:ControlServoWithComputer.c

● 輸入、保存、下載并運行程序ControlServoWithComputer.c;

● 驗證是否機器人各個輪子的轉(zhuǎn)動是不是同你期望的運動一樣。

      #include "stm32f10x_heads.h"
      #include "HelloRobot.h"
      unsigned int USART_Scanf()
      {
        u16 index = 0;
        u16 recdata;
        u16 tmp[5]={0,0,0,0,0};
        while(1)
        {
          /* Wait until RXNE = 1 */
          while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);
          tmp[index] = (USART_ReceiveData(USART1));
        if(tmp[index] == '#')
        {
          if(index!=0)   break;
          else        continue;
          }
        index++;
        }
        /* Calculate the Corresponding value */
        if(index==1)
        recdata = (tmp[0] -0x30);
        if(index==2)
        recdata = (tmp[1] -0x30) + ((tmp[0] -0x30) * 10);
        if(index==3)
        recdata = (tmp[2] -0x30) + ((tmp[1] -0x30) * 10)+ ((tmp[0] -0x30) * 100);
        if(index==4)
        recdata = (tmp[3] -0x30) + ((tmp[2] -0x30) * 10)+ ((tmp[1] -0x30) * 100)+ ((tmp[0] -0x30) *
1000);
        return recdata;
      }
      int main(void)
      {
        int Counter;
        unsigned int PulseNumber,PulseDuration;
        BSP_Init();
        USART_Configuration();
        printf("Program Running!\r\n");
        printf("Please input pulse number:\r\n");
        PulseNumber=USART_Scanf();
        printf("Input pulse number is %d\r\n",PulseNumber);
        printf("Please input pulse duration:\r\n");
        PulseDuration=USART_Scanf();
        printf("Input pulse duration is %d\r\n",PulseDuration);
        for(Counter=1;Counter<=PulseNumber;Counter++)
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_10);
            delay_nus(PulseDuration);
            GPIO_ResetBits(GPIOD,GPIO_Pin_10);
            delay_nms(20);
        }
        for(Counter=1;Counter<=PulseNumber;Counter++)
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_9);
            delay_nus(PulseDuration);
            GPIO_ResetBits(GPIOD,GPIO_Pin_9);
            delay_nms(20);
        }
        while(1);
      }

ControlServoWithComputer.c是如何工作的?

在這個程序中,單片機不僅向串口傳送信息給你看,而且還通過串口從計算機讀取輸入的數(shù)據(jù)。串口接收數(shù)據(jù)函數(shù)USART_Scanf的工作機制將在后面的章節(jié)講解,其作用是從計算機接收數(shù)據(jù),計算機向單片機發(fā)出的數(shù)據(jù)以#作為結(jié)束標記,就像使用充值卡給電話(手機)充值時,輸入完賬號和密碼按#表示結(jié)束,如圖2.14所示。

圖2.14 例程運行過程

注意break和continue的區(qū)別:break是結(jié)束整個循環(huán)體,而continue是結(jié)束本次循環(huán)。

(1)首先輸出“Program Running!”和“Please input pulse number:”;

(2)程序處于等待狀態(tài),等待你從串口調(diào)試軟件輸入數(shù)據(jù),數(shù)據(jù)以“#”作為結(jié)束標記;

(3)將輸入的數(shù)據(jù)給變量PulseNumber,再把這個數(shù)據(jù)回傳給計算機顯示;

(4)輸出“Please input pulse duration:”;

(5)又處于等待狀態(tài),等待你從串口調(diào)試軟件再次輸入數(shù)據(jù),數(shù)據(jù)以“#”作為結(jié)束標記;

(6)將輸入的數(shù)據(jù)給變量PulseDuration,再把這個數(shù)據(jù)回傳給計算機顯示;

(7)電機運轉(zhuǎn)。

主站蜘蛛池模板: 五莲县| 铜梁县| 玉屏| 三穗县| 黔江区| 蓝田县| 安多县| 汕尾市| 荣成市| 万荣县| 开远市| 改则县| 奎屯市| 阳江市| 苏尼特右旗| 健康| 武穴市| 平谷区| 丹江口市| 庄河市| 洪雅县| 桐庐县| 宁远县| 郎溪县| 隆回县| 喀什市| 体育| 金堂县| 新和县| 旌德县| 凭祥市| 乐亭县| 多伦县| 扎兰屯市| 建始县| 纳雍县| 维西| 司法| 泊头市| 阿拉善右旗| 石门县|