- 基于ARM Cortex-M3的STM32系列嵌入式微控制器應用實踐
- 彭剛 秦志強編著
- 126字
- 2018-12-27 16:01:48
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)。
- 用Proteus可視化設(shè)計玩轉(zhuǎn)Arduino
- 單片機應用技術(shù)
- 單片機原理與應用:基于Keil+Proteus
- PIC單片機常用模塊與綜合系統(tǒng)設(shè)計實例精講
- AVR單片機實用程序設(shè)計
- 計算機與嵌入式系統(tǒng)架構(gòu)
- 51單片機工程師是怎樣煉成的:基于C語言+Proteus仿真
- 單片微型計算機原理及應用
- AVR單片機原理與應用實例
- 嵌入式系統(tǒng):基于項目的分析和設(shè)計
- 深度學習實踐教程
- 基于ARM Cortex-M0+的CW32嵌入式開發(fā)實戰(zhàn)
- AVR單片機很簡單:C語言快速入門及開發(fā)實例
- 單片機原理與應用技術(shù)
- 高分辨率遙感影像變化檢測