顯示具有 機械手臂控制篇 標籤的文章。 顯示所有文章
顯示具有 機械手臂控制篇 標籤的文章。 顯示所有文章

2008年12月3日 星期三

機械手臂控制篇(10) -- 操作界面篇

1977649482



完成了!!

VB6的界面

原始程式下載



操作的方法很簡單

就是用[+]跟[-]調整SERVO的位置

用test motion看一下是不是自己要的

再按一下save motion

這樣就可以了



要執行的話

按一下run motion XXX to XXX

就可以執行已經儲存好的設定



最後如果要更新動作

就按一下clear all motions

再重新儲存motion

這樣就大功告成了



目前每一個動作的時間間隔是0.25秒

要增加停頓的時間

就把該動作連續存幾次就可以了





照慣例要來個影片

請笑納







ps1.

不要太期待會有很穩定的動作

因為SERVO本身光是送固定寬度的PWM就已經抖個不停

更別提要連續改變SERVO的角度

應該是慘不忍睹的

參考一下就好了~~~~





ps2.

程式其實有預留一些函數沒有呼叫

包含將動作存到檔案

漸進式傳送角度的功能

這些都還沒掛上

所以看動作的時候會比較粗糙

晃動也比較利害

2008年12月1日 星期一

機械手臂控制篇(9) -- 控制板組裝篇

在撰寫PC操作界面程式之前

先把手臂的控制板先組裝起來吧!!



記得一開始所提到的洞洞板嗎?

長得跟MCU板一樣大小的那一片?



由於板子的空間真的很小

所以我就放棄原來要加74HC14的作法

改用一般最常做的方式

(其實是因為我的MCU程式已經寫成正邏輯驅動的關係 )



看一下焊好的樣子吧~~~



1977649475

這一張是正面的樣子

開關的部分我用指撥開關

由於有兩個馬達要同訊號

所以有兩個接頭是共用一個指撥開關的

而指撥開關所管制的就是每一組SERVO的電源

(目前有四組)



1977649476

因為電流不算小

本來想用鍍銀線

後來全部都用AWG24的線來上!!

焊得挺累的~~



1977649478

接下來把MCU板焊到洞洞板上

就像圖中所看到的一樣

這可是特別設計過的唷~~~



1977649479

看看背面的焊線吧!!!

信號線(橙)稍微用小一點

是AWG26的線



1977649480

連接好的總圖就是這樣囉!!!





在焊接的過程想到一個問題

為啥SERVO的電源大多都設計在中間腳與一側腳

卻不是將電源放在兩側

而信號跑中間??



焊接跟PCB LAYOUT時

你就知道為啥了!!!









1977649481

最後來看張大合照

萬事皆備

只欠東風

界面程式加油

就要到結尾囉!!!





ps1.

提醒一下

焊接類似這樣的小東西

特別是線一堆的板子

每焊好一條就要用電表量一下

是否點到點有導通

不然等全部焊完才發現最裡面的線沒焊好.....

那就暈暈暈暈暈.........

倒倒倒倒倒.........

Orz...........





ps2.

通電後

MG995的手臂一直在搖

它對信號的截取一直很不穩定

所以在寫界面程式的時候

記得千萬別一次動太多個SERVO

也絕對不要一次跳太多解析度

因為光是要維持動作

它的電流就已經在0.03~0.125A在跳動

扣掉MCU不到0.01A的電流

再次讓人感受到MG995被罵的劣根性

一分錢一分貨

千萬不要貪便宜唷~~~

2008年11月30日 星期日

機械手臂控制篇(8) -- 通訊連線動作篇

測試的過程不要急躁

先從改變一個SERVO的信號開始就好



1977649468

在AccessPort上鍵入"00" "FF"

再按下[發送資料]



1977649469

在示波器上會看到這樣的波形

黃色是UART的信號

而粉紅色是PWM改變後的寬度

這樣看也許體會不到

再看一張送"00" "01"的圖



1977649470

有看到了嗎?

SERVO的PWM寬度明顯縮小了!!!



有發現我送信號都是送"00" "XX"嗎?

這就是在程式中加入歸零的好處

我可以每次都改變SERVO1的PWM就好

不用一次都改變四個SERVO的值

當然如果要改到後面的SERVO還是要四個一起送

但在測試時

這樣就可以節省不少時間唷~~~



不過通訊碼當然不能只是這麼簡單

未來改版再慢慢考慮編碼的問題

現下先讓信號正確就好



1977649471

接下來把SERVO1~4的測棒都接上

實際看一下信號的改變

在這裡所輸入的數值都是16進位的唷

請自行用工程型的小算盤換算一下



1977649472

看到了嗎?

四組SERVO的信號改變了!!!



1977649473

再換一組數值看看



1977649474

不錯吧!!

連線成功~~~~

可以開始撰寫pc的操作介面了!!!



ps.

記得剛剛說過的嗎?

不要急躁!!

如果現在你急著接到SERVO上看動作

很可能還是會有災難發生唷~~~



驗證步驟是要按步就班的

今天先到此為止囉!!

機械手臂控制篇(7) -- 通訊硬體改裝篇

因為手邊沒有更完整的板子

所以只好自己拼裝

還記得之前介紹的USB轉UART工具嗎?



現在就要透過他來進行連線的測試囉

在這之前要先作個小改裝



1977649463

這一張是轉接板要焊上的跳線

記得要把TxD與GND都接上MCU的RxD與GND唷

這樣信號才有參考準位

不然如果只接TxD與MCU的RxD

那是一定不會通的!!!

(會亂入~~~)



1977649464

MCU這端的接線就是這樣

有空一點再把線路圖整理出來給大家看



1977649465

連好之後就像這樣

同時把示波器跟測棒也準備好





到此我們就可以開始進行測試了

在旗威論壇中有介紹一套很好用的[AccessPort]

底下就來試用一下

順便提供一下download的地方好了

[AccessPort下載]



1977649466

首先要設定一下COMPORT的位址

可以從裝置管理員中的連接埠找到

我的是COM4

所以就選COM4

鮑率按照前一篇程式所設定的9600,N,8,1



1977649467

從PC上送出資料後

確認TxD腳位上有信號出來

這樣就算OK了~~~~

接下來要測試SERVO信號囉!!!





ps.

如果您在示波器上看不到這個信號

那有下列幾種可能



1. 示波器的觸發準位設定不對



2. 確認MCU的程式中設定IO狀態是對的, UART的功能有確實被打開



3. 兩邊的GND是否有接上?沒有共地信號是會亂掉的....



4. 其它狀況請回應讓我知道, 我來幫您看看~~~  ^+++++^

機械手臂控制篇(6) -- 通訊測試MCU程式篇

終於要開始寫通訊了

相信這一段是很多人心中的痛

並不是通訊連不上

而是連上了卻不正確動作



其中有很多設定的部分是要重覆確認的(Double Check)

程式的中斷也是很重要的部分

更重要的是程式的編碼組成



為了簡化程式困難度

所以我採用四個Byte 的通訊

每個Byte以1~255來決定SERVO的角度

而0是一個特別指令

用來將信號歸零用

這個功能的優點

大家稍後就會知道了~~~



先來看看程式吧!!

//-----------------------------------------------------------------------------

// ROBO_ARM.c

//-----------------------------------------------------------------------------

// AUTH: Nichal

// DATE: 2008/11/30

//

// Target: C8051F30x

// Tool chain: KEIL C51 6.03 / KEIL EVAL C51

//



//-----------------------------------------------------------------------------

// Includes

//-----------------------------------------------------------------------------



#include // SFR declarations



//-----------------------------------------------------------------------------

// 16-bit SFR Definitions for 'F30x

//-----------------------------------------------------------------------------



sfr16 DP = 0x82; // data pointer

sfr16 TMR2RL = 0xca; // Timer2 reload value

sfr16 TMR2 = 0xcc; // Timer2 counter



//-----------------------------------------------------------------------------

// Global CONSTANTS

//-----------------------------------------------------------------------------



#define SYSCLK 24500000 // SYSCLK frequency in Hz

#define BAUDRATE 9600 // Baud rate of UART in bps

#define SAMPLE_RATE 50 // Sample frequency in Hz

#define SERVO1_PWM SERVO1=1

#define SERVO2_PWM SERVO2=1

#define SERVO3_PWM SERVO3=1

#define SERVO4_PWM SERVO4=1



#define TMH (65536-(SYSCLK/12/SAMPLE_RATE))/256

#define TML (65536-(SYSCLK/12/SAMPLE_RATE))%256



#define MAX_ANGLE 62200

#define MIN_ANGLE 12400

#define MID_ANGLE 37300



sbit SERVO1 = P0^0;

sbit SERVO2 = P0^1;

sbit SERVO3 = P0^2;

sbit SERVO4 = P0^3;



//-----------------------------------------------------------------------------

// Function PROTOTYPES

//-----------------------------------------------------------------------------



void SYSCLK_Init (void);

void UART0_Init (void);

void UART0_ISR (void);

void PORT_Init (void);

void Timer0_Init (void);

void Timer0_ISR (void);

void Timer2_Init (void);

void Timer2_Switch (unsigned int counts);

void Timer2_ISR (void);



//-----------------------------------------------------------------------------

// Global VARIABLES

//-----------------------------------------------------------------------------



bit start_flag;

bit change_flag;

unsigned char servo_counts;

unsigned int S[4]={MID_ANGLE,MID_ANGLE,MID_ANGLE,MID_ANGLE};

//-----------------------------------------------------------------------------

// MAIN Routine

//-----------------------------------------------------------------------------



void main (void)

{

// Disable Watchdog timer

PCA0MD &= ~0x40; // WDTE = 0 (clear watchdog timer enable)

SYSCLK_Init (); // initialize oscillator

PORT_Init (); // initialize crossbar and GPIO



Timer2_Init ();

Timer0_Init ();

start_flag=0;

change_flag=0;

servo_counts=0;

UART0_Init ();

EA=1;



while (1)

{

if (start_flag)

{

start_flag=0;

SERVO1_PWM;

Timer2_Switch(S[0]);



while (change_flag==0);

change_flag=0;

SERVO2_PWM;

Timer2_Switch(S[1]);



while (change_flag==0);

change_flag=0;

SERVO3_PWM;

Timer2_Switch(S[2]);



while (change_flag==0);

change_flag=0;

SERVO4_PWM;

Timer2_Switch(S[3]);



while (change_flag==0);

change_flag=0;

// add new servo here

}

}

}



//-----------------------------------------------------------------------------

// Initialization Subroutines

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

// SYSCLK_Init

//-----------------------------------------------------------------------------

//

// This routine initializes the system clock to use the internal 24.5MHz

// oscillator as its clock source. Also enables missing clock detector reset.

//

void SYSCLK_Init (void)

{

OSCICN |= 0x03; // configure internal oscillator for

// its maximum frequency}

RSTSRC = 0x04; // enable missing clock detector

}



//-----------------------------------------------------------------------------

// UART0_Init

//-----------------------------------------------------------------------------

//

// Configure the UART0 using Timer1, for and 8-N-1.

//

void UART0_Init (void)

{

SCON0 = 0x10; // SCON0: 8-bit variable bit rate

// level of STOP bit is ignored

// RX enabled

// ninth bits are zeros

// clear RI0 and TI0 bits

if (SYSCLK/BAUDRATE/2/256 < 1) {

TH1 = -(SYSCLK/BAUDRATE/2);

CKCON |= 0x10; // T1M = 1; SCA1:0 = xx

} else if (SYSCLK/BAUDRATE/2/256 < 4) {

TH1 = -(SYSCLK/BAUDRATE/2/4);

CKCON |= 0x01; // T1M = 0; SCA1:0 = 01

CKCON &= ~0x12;

} else if (SYSCLK/BAUDRATE/2/256 < 12) {

TH1 = -(SYSCLK/BAUDRATE/2/12);

CKCON &= ~0x13; // T1M = 0; SCA1:0 = 00

} else {

TH1 = -(SYSCLK/BAUDRATE/2/48);

CKCON |= 0x02; // T1M = 0; SCA1:0 = 10

CKCON &= ~0x11;

}



TL1 = 0xff; // set Timer1 to overflow immediately

TMOD |= 0x20; // TMOD: timer 1 in 8-bit autoreload

TMOD &= ~0xD0; // mode

TR1 = 1; // START Timer1

IP |= 0x10; // Make UART high priority

ES0 = 1; // Enable UART0 interrupts

}

//-----------------------------------------------------------------------------

// UART0_ISR

//-----------------------------------------------------------------------------

//

//

void UART0_ISR (void) interrupt 4

{

unsigned char x;



if(RI0)

{

RI0=0;

x=SBUF0;

if (x)

{

S[servo_counts]= ((unsigned int)(x)*190) + 12980;

servo_counts++;

if (servo_counts>3) servo_counts=0;

}

else servo_counts=0;

}



if (TI0) TI0=0;

}



//-----------------------------------------------------------------------------

// PORT_Init

//-----------------------------------------------------------------------------

//

void PORT_Init (void)

{

XBR0 = 0xdf;

XBR1 = 0x02;

XBR2 = 0x40;

P0MDOUT = 0xdf;

P0MDIN = 0xff;

P0 &= 0xf0;

}




//-----------------------------------------------------------------------------

// Timer0_Init

//-----------------------------------------------------------------------------

// 20mS Timer

//

void Timer0_Init (void)

{

TCON &= 0xcf; // Stop Timer0; Clear TF0

CKCON &= ~0x08; // Timer0 uses SYSCLK/12

TMOD |= 0x01; // Timer0 in 16-bit mode

TH0 = TMH; // 20mS interrupt

TL0 = TML;

ET0 = 1; // enable Timer0 interrupts

TR0 = 1; // Timer0 enabled

}



//-----------------------------------------------------------------------------

// Timer0_ISR

//-----------------------------------------------------------------------------

//

//

void Timer0_ISR (void) interrupt 1 // wheel signal off

{

TF0 = 0;

TH0 = TMH;

TL0 = TML;



start_flag=1;

}

//-----------------------------------------------------------------------------

// Timer2_Init

//-----------------------------------------------------------------------------

// PWM Timer

//



void Timer2_Init (void)

{

TMR2CN = 0x00;

CKCON |= 0x20;

ET2 = 0;

TR2 = 0;

}



void Timer2_Switch (unsigned int counts)

{

TMR2 = -counts; // set to reload immediately

ET2 = 1; // disable Timer2 interrupts

TR2 = 1;

}

//-----------------------------------------------------------------------------

// Timer2_ISR

//-----------------------------------------------------------------------------

//



void Timer2_ISR (void) interrupt 5

{

TF2H = 0; // clear Timer2 interrupt flag

TR2 = 0;

P0 &= 0xf0;

change_flag=1;

}





顏色與眾不同的

就是新加入或修改的部分

接下來談一談為啥要這樣規劃程式





1. 中位點的運算



大家都知道

所謂的一個Byte

指的是8 bit

由2的八次方來看

它可以代表1~256

然而實際上在MCU裡

並沒有256這個值

它所對應的是0~255



以中位點來說

(0+255)/2=127.5

如果程式碼要決定中位點

必須送出一個127.5的值

請問大家這個值要怎麼送???



答案是

我也不會!!! ^+++++++^



因為MCU的每一個值都只能定義整數

小數點是我們自己幫它加上去的!!!

如果要提高到127.5

就必須多一個Byte讓它變成65535之後

以1275去加小數點



為了讓中位點更中位

所以我用1~255來表示

又最大與最小值的間隔是62200-12400=49800

以49800/256會得到 194.xxxxxx

所以我直接取190做為每一個count的時間間隔

在UART0_ISR的程式中可以看到





2. 預防指令誤傳的法寶



同樣在UART0_ISR的程式中

有一個if (x)..... else servo_counts=0;

為什麼要加這個動作呢?



我們先分析一下這個UART的中斷裡

SERVO的值是如何被改變的:



首先是servo_counts=0

所以當你收到第一個Byte的資料後

你會把資料填在S[0]裡面

接著把servo_counts加1

再接收下一個byte

放在S[1]....

依序放完4個Byte後

servo_counts會自己歸零

完美的寫法!!!

????



如果不小心多送一個byte

會怎樣???

那災難就會發生了!!!!

因為往後所有的信號都會錯開一個Byte

送給第一個servo的信號會被第二個servo用

.......



你或許會問

怎麼可能會多送一個Byte?



我也覺得不可能

可是有時候因為信號線被誤觸

多一個或少一個信號是常有的事

這時候系統就要夠聰明

多一個防呆動作

利用0來將信號歸零後再繼續動作

確保就算這一次不小心送錯了

下一次也絕對不要一直錯下去





3. 其它部分



原則上多了一個數據陣列

用來儲存SERVO的角度

預設是停在中位點

大家看一下宣告就知道了



IO的設定要小心

F300在這方面很講究

設錯了就不會動作



大致上就這樣囉~~~

下一篇再來看看如何動作的

2008年11月29日 星期六

機械手臂控制篇(5) -- 通訊前的準備

機械手臂的前置作業已經做得差不多了

接下來就是通訊的部分了

在正式通訊之前

先來說明PC與MCU之間是如何溝通的



首先是MCU的部分

一般來說

MCU最常用的通訊功能是UART

它是透過RxD與TxD的通訊腳位進行通訊



然而PC雖然也有所謂的RxD與TxD

卻沒辦法與MCU直接溝通

因為PC的通訊界面稱為RS232

它的工作電壓是DC12V

而一般MCU是DC5V或DC3.3V

所以如果直接通訊的話

MCU馬上會罷工給你看!!!!!



那該怎麼辦?

這時候就有一種叫做RS232轉UART的工具

用來把RS232的信號準位轉變成UART的準位

這樣就可以通訊了



常見的轉換IC有MAX232 , HIN232....等等

底下提供他們的相關資料給大家參考

MAX232相關資料

HIN232相關資料





不過

隨著時代在進步

很多個人電腦已經放棄RS232介面

全面改成USB介面了

而MCU改朝換代後

很多也都內建USB的功能

這意謂著學通訊的人

要開始接受新的科技洗禮

不然很可能會慢慢變成古"懂"

(就是懂的東西都作古了!!)



雖然時代在轉變

但還是會有一些變遷性的產物

不然過去那麼多的MCU一下子失去支援

很可能全部都變成廢品

(就像無鉛的禁令一出, 很多有鉛品馬上變成廢料.....)

這比不做資源回收還恐怖!!!



這樣的產物叫做USB轉UART

在台灣最早出現的是PL-2303

這一個也是現在USB轉RS232用得最多的晶片

該晶片的驅動也已經通過微軟認證

只要上網執行Windows Update就可以完成驅動

PL-2303相關資料



全台灣第一台USB的AT89C2051燒錄器

用的也是這一個晶片

(就是小弟公司所生產的PGM2051啦)

此外

第一代的USB轉RS485轉接盒也是用這個晶片

換句話說

早期台灣很多標榜USB的產品

骨子裡並不真的是USB架構的東西

而是包了一個USB的轉換介面

讓它可以相容於USB的通訊格式

實際用的還是UART的通訊方式!!!



1977649462

這個就是第一代的USB轉RS485的片段照片

圖中黑黑的一顆SMD元件就是它的樣子



不過雖然它有這麼輝煌的豐功偉業

在公司裡也已經不再使用這個晶片了

畢竟科技真的是一直在進步

不是嗎?

現下用的晶片是Silicon Labs所出的

至於是哪一個

大家去查一查就知道啦~~~





好像又離題了

再回到主題.....





有了USB轉UART的功能

就可以讓電腦與MCU搭起友誼的橋樑

聽起來很神奇齁?

下一篇文章我們就來實際看看

到底PC與MCU到底是如何透過轉換介面通訊的

敬請拭目以待





ps.

對USB架構有興趣的朋友

有一個人你們一定要認識

讓我鄭重向各位推薦

chamber大大的網站

http://chamberplus.myweb.hinet.net/

他是USB界的神人

或者

說魔人更為恰當~~~

2008年11月26日 星期三

機械手臂控制篇(4) -- 程式基本功能PWM

下面這個程式是目前剛完成PWM基本測試的內容

使用的MCU大家應該不陌生-- C8051F300

大致上就是輪替送出PWM的功能





//-----------------------------------------------------------------------------

// ROBO_ARM.c

//-----------------------------------------------------------------------------

// AUTH: Nichal

// DATE: 2008/11/27

//

// Target: C8051F300

// Tool chain: KEIL C51 6.03 / KEIL EVAL C51

//



//-----------------------------------------------------------------------------

// Includes

//-----------------------------------------------------------------------------



#include // SFR declarations



//-----------------------------------------------------------------------------

// 16-bit SFR Definitions for 'F30x

//-----------------------------------------------------------------------------



sfr16 DP = 0x82; // data pointer

sfr16 TMR2RL = 0xca; // Timer2 reload value

sfr16 TMR2 = 0xcc; // Timer2 counter



//-----------------------------------------------------------------------------

// Global CONSTANTS

//-----------------------------------------------------------------------------



#define SYSCLK 24500000 // SYSCLK frequency in Hz

#define BAUDRATE 9600 // Baud rate of UART in bps

#define SAMPLE_RATE 50 // Sample frequency in Hz

#define SERVO1_PWM SERVO1=1

#define SERVO2_PWM SERVO2=1

#define SERVO3_PWM SERVO3=1

#define SERVO4_PWM SERVO4=1




#define TMH (65536-(SYSCLK/12/SAMPLE_RATE))/256

#define TML (65536-(SYSCLK/12/SAMPLE_RATE))%256



#define MAX_ANGLE 62200

#define MIN_ANGLE 12400

#define MID_ANGLE 37300



sbit SERVO1 = P0^0;

sbit SERVO2 = P0^1;

sbit SERVO3 = P0^2;

sbit SERVO4 = P0^3;




//-----------------------------------------------------------------------------

// Function PROTOTYPES

//-----------------------------------------------------------------------------



void SYSCLK_Init (void);

void PORT_Init (void);

void Timer0_Init (void);

void Timer0_ISR (void);

void Timer2_Init (void);

void Timer2_Switch (unsigned int counts);

void Timer2_ISR (void);



//-----------------------------------------------------------------------------

// Global VARIABLES

//-----------------------------------------------------------------------------



bit start_flag;

bit change_flag;

unsigned char servo_counts;



//-----------------------------------------------------------------------------

// MAIN Routine

//-----------------------------------------------------------------------------



void main (void)

{

// Disable Watchdog timer

PCA0MD &= ~0x40; // WDTE = 0 (clear watchdog timer enable)

SYSCLK_Init (); // initialize oscillator

PORT_Init (); // initialize crossbar and GPIO

Timer2_Init ();

Timer0_Init ();

start_flag=0;

change_flag=0;

servo_counts=0;

EA=1;



while (1)

{

if (start_flag)

{

start_flag=0;

SERVO1_PWM;

Timer2_Switch(MID_ANGLE);



while (change_flag==0);

change_flag=0;

SERVO2_PWM;

Timer2_Switch(MID_ANGLE);



while (change_flag==0);

change_flag=0;

SERVO3_PWM;

Timer2_Switch(MID_ANGLE);



while (change_flag==0);

change_flag=0;

SERVO4_PWM;

Timer2_Switch(MID_ANGLE);



while (change_flag==0);

change_flag=0;

// add new servo here



}

}

}



//-----------------------------------------------------------------------------

// Initialization Subroutines

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

// SYSCLK_Init

//-----------------------------------------------------------------------------

//

// This routine initializes the system clock to use the internal 24.5MHz

// oscillator as its clock source. Also enables missing clock detector reset.

//

void SYSCLK_Init (void)

{

OSCICN |= 0x03; // configure internal oscillator for

// its maximum frequency}

RSTSRC = 0x04; // enable missing clock detector

}



//-----------------------------------------------------------------------------

// PORT_Init

//-----------------------------------------------------------------------------

//

void PORT_Init (void)

{

XBR0 = 0xff;

XBR1 = 0x00;

XBR2 = 0x40;

P0MDOUT = 0xff;

P0MDIN = 0xff;

P0 = 0;

}



//-----------------------------------------------------------------------------

// Timer0_Init

//-----------------------------------------------------------------------------

// 20mS Timer

//

void Timer0_Init (void)

{

TCON &= 0xcf; // Stop Timer0; Clear TF0

CKCON &= ~0x08; // Timer0 uses SYSCLK/12

TMOD |= 0x01; // Timer0 in 16-bit mode

TH0 = TMH; // 20mS interrupt

TL0 = TML;

ET0 = 1; // enable Timer0 interrupts

TR0 = 1; // Timer0 enabled

}



//-----------------------------------------------------------------------------

// Timer0_ISR

//-----------------------------------------------------------------------------

//

//

void Timer0_ISR (void) interrupt 1 // wheel signal off

{

TF0 = 0;

TH0 = TMH;

TL0 = TML;



start_flag=1;

servo_counts=0;

}

//-----------------------------------------------------------------------------

// Timer2_Init

//-----------------------------------------------------------------------------

// PWM Timer

//



void Timer2_Init (void)

{

TMR2CN = 0x00;

CKCON |= 0x20;

ET2 = 0;

TR2 = 0;

}



void Timer2_Switch (unsigned int counts)

{

TMR2 = -counts; // set to reload immediately

ET2 = 1; // disable Timer2 interrupts

TR2 = 1;

}

//-----------------------------------------------------------------------------

// Timer2_ISR

//-----------------------------------------------------------------------------

//



void Timer2_ISR (void) interrupt 5

{

TF2H = 0; // clear Timer2 interrupt flag

TR2 = 0;

P0 = 0;

change_flag=1;

}





看完一大片程式碼

有沒有眼花撩亂

沒關係

改看看輸出的波形測試



1977649456

程式中的Timer0是用來產生歸零動作的

也就是所謂的20mS信號

不過因為內部振盪器的誤差

所以實際值只有19.7mS

這個值是可以調整的

只是因為我的需求到這樣就夠了

所以就沒有再修過



1977649457

看看這一張

非常準確的1.5mS

代表信號是真的可以修得非常準的

只是需不需要修到這麼準罷了!!

這個信號是用MID_ANGLE的參數所產生的波形



1977649458

而這一張0.5mS(500uS)

則是用MIN_ANGLE所產生的波形



1977649459

再來看一張2.5mS

是用MAX_ANGLE所產生的波形





1977649460

為了看看輪替輸出的樣子

把所有的測棒通通都接上控制板



1977649461

這就是最後輸出的樣子

因為只有四根測棒

所以先開4CH的PWM給大家看看就好



其實這一類的程式

很多網頁或書籍都有說明跟範例

不過絕大多數會用同一個埠來輪替輸出

以運算的方式強制從該埠的第0腳到第7腳輸出

然而對於腳位較少

而且腳位分布不固定的MCU

(例如一個埠只有0,3,4,6,7這幾隻腳)

這樣的寫法會比較有彈性

只要每增加一個 SERVO

在程式的最前方宣告一下

#define SERVOX_PWM SERVO1=1

sbit SERVOX=腳位;


然後再到主程式裡

標記// add new servo here的地方

再加上一段程式碼

************************************

SERVOX_PWM;

Timer2_Switch(MID_ANGLE);



while (change_flag==0);

change_flag=0;

************************************


這樣就可以再新增一組控制項(多一組PWM出來)

其中MID_ANGLE的參數是可以修正的

對應到的是實際需求的角度信號



有了這個基本功能後

接下來就要加入通訊功能

這樣才有辦法透過PC來控制多變的機械手臂動作

敬請期待~~~



ps.

最近工作比較忙

沒辦法天天更新

請大家多多包涵

2008年11月21日 星期五

機械手臂控制篇(3) -- 控制電路的規劃

使用越低階的SERVO

電源的管制是越重要的

高階的SERVO會把電源的穩定性考慮進去

因而內建限流及穩壓的線路

但低階SERVO為了成本考量

在這方面的設計就變得比較粗糙

我們常抱怨的馬達抖動

很可能是在電源變動時造成的



1977649455

這是一個控制電路的示意圖

以8051來說

剛開機時的輸出一定是High(高位準)

透過反相器可以避開假信號所造成的誤動作(另一個抖動的原因)

不過信號要改成圖中反相送出的方式



電源端加上一個開關

是為了避開之前所提到的

當同時很多SERVO一起通電

會降低供應電壓

導致MCU的電壓不足

而當機發生誤動作



但是

如果您採用分開電源的管理

就是將MCU控制端與馬達出力端的電源分開

那這個影響就比較小

可以再省下開關元件(但就會多一組電源)



由於我的手臂有六個SERVO

而7414剛好也有六組反相器

不然單一SERVO可以用電晶體取代就好



這樣的電路設計

其實是針對目前我所用的SERVO所規劃

有些高階的SERVO只要單純灌一組pulse就可以了

其它都不用考慮

一分錢一分貨啦!!!

機械手臂控制篇(2) -- 活動角度的確認








在控制之前

確認機械手臂的活動角度是很重要的!!



放上一個影片給大家瞧瞧

我的手臂是全角度都沒有干涉的唷!!

(因為唯一會干涉的手掌昨天被我玩壞了!! Orz...)



測試的工具就是PWM測試儀

而且很猛的是

我只用一根pin的信號同時控制五個servo



不過請記得要開機前

所有的SERVO線要先移除

不然一開機災難就來了!!!

(我的手掌就是在這種情況下陣亡的)



開機後

請一個一個servo慢慢加上去

同軸的兩個SERVO要一起上

免得手臂被扯斷

2008年11月20日 星期四

機械手臂控制篇(1) -- MCU的選用

1977649452

這塊板子就是我即將用在手臂上的控制板

會有上下兩層

先給大家預覽一下~~~



1977649453

沒看到MCU??

在背面啦!!!



這個就是傳說中的F300

內容是8051 Base

有興趣的可以去查查看datasheet



電源的部分還沒確定

應該會用電池直接驅動



先到這裡

留給大家一些想像的空間~~~

^+++++++^