下面這個程式是目前剛完成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;
}
看完一大片程式碼
有沒有眼花撩亂
沒關係
改看看輸出的波形測試
程式中的Timer0是用來產生歸零動作的
也就是所謂的20mS信號
不過因為內部振盪器的誤差
所以實際值只有19.7mS
這個值是可以調整的
只是因為我的需求到這樣就夠了
所以就沒有再修過
看看這一張
非常準確的1.5mS
代表信號是真的可以修得非常準的
只是需不需要修到這麼準罷了!!
這個信號是用MID_ANGLE的參數所產生的波形
而這一張0.5mS(500uS)
則是用MIN_ANGLE所產生的波形
再來看一張2.5mS
是用MAX_ANGLE所產生的波形
為了看看輪替輸出的樣子
把所有的測棒通通都接上控制板
這就是最後輸出的樣子
因為只有四根測棒
所以先開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.
最近工作比較忙
沒辦法天天更新
請大家多多包涵
大大,請問一下,這可以移植到2051或8051嗎?
回覆刪除2051有兩個Timer
刪除如果要移植的話
串列的Timer1就先犧牲了
用剩下的拆成一個8bit 與一個16bit
理論上還是可行
只是解析度就會變差很多
而且就沒辦法擴充其它功能了
8051跟2051差不多
只是IO多了一點
建議是用8052會比較好
那就應該可以整個調整過去
因為有Timer0,1,2三組可用
但一樣還是會有解析度的問題
keil c跟普通的c是一樣的嗎?
回覆刪除不太懂XD
C有分寫給低階韌體用的
刪除也有寫給高階應用程式用的
Keil C是針對MCU開發專用的工具
而VC或BCB是一般PC介面用的工具
大概就這樣囉
差別只在於編譯器不同
語法是一樣的
請問一下 我們是XX科技大學的學生
回覆刪除我們想知道你是如何製作你的手臂(尤其是軟體的部分)
我們與您一樣是使用keil C 的程式撰寫 但我們對程式一竅不通
方便留一下你的E-MAIL或是電話 我們想要依依地請教你
在這裡留言就可以了 ~~~
刪除不方便公開的話就用悄悄話
對程式不懂的話
建議先看一下Keil C的說明文件 (C51.pdf)
軟體的部分其實分兩大部分
一個是手臂本體 (就是你要問的部分)
另一個是PC端的控制程式 (就是最後要用電腦控制的部分)
看你的完成度要到哪~~~
如果想學快一點
右邊有個 "小鯨魚的機器人" 的連結
裡面的小鯨魚是專職的程式教師
也是專門在做機器人與機械手臂的
直接與他聯繫會更快得到你想知道的答案唷~~~~
不過有任何問題都還是隨時歡迎來這裡與我交流
祝你順利~~~
板大 我想請問一下 因為我完全沒有單晶片基礎 有學過c 市面上有哪本書 有詳細解說並有範例
回覆刪除由淺入深 完全用c去撰寫的呢?? 找了好幾本 裡面大部分都適用組合語言去寫的 c的部分提的很
少...看完也沒有範例可以練習..
PIC真的比較多人寫組語
刪除因為指令比較少
如果用C寫
大概不能只看PIC的書
而是要上網找範例
一般來說
單晶片的C跟電腦上的C其實差不多
最大的差異是在Head檔的引用與暫存器的用法
假如你已經有C的基礎
那麽你只要熟悉暫存器的功能就可以了
這個看組語的書會更好
不須要去看指令
只要看他對暫存器如何設定
用C來寫的話
就是把暫存器當成變數
指定一個值給它的話就是用"="就可以了
比方說 ACC=1;
就是把累加器的值設定為1
之所以很少有全部用C來寫的書
是因為再怎麼少
有一些動作還是要用組語來替代
單純用C轉不出某些碼
你就慢慢習慣吧~~~
剛開始都會比較不好懂
多看幾遍就會了
上一篇忘記打name.. 也忘記提到 用的pic是18f8720
回覆刪除原廠其實蠻多用C寫的範例
刪除你不妨去下載看看
針對每個功能應該都有對應的C code寫法
拿那個來改看看
應該會比較容易進入狀況
祝你順利囉~~~
看C的範例不要局限在特定的IC編號上
因為用C寫的差異並不大
不一樣的只有暫存器的名稱跟用法而已
語法跟運算看起來應該都是一樣的
你好~我在網路有找到 先生好像有
回覆刪除全華 機器人 單晶片微電腦控制 書的東西
可不可以麻煩跟你拿一下 光碟裡面的程式
印象中我的好像只有附一片PCB
刪除程式都自己Keyin
所以也不記得是不是有光碟
我再找看看好了
說不定是忘了丟在哪裡了
找到再跟你說
BTW
我的書是綠色面的
跟現在的藍色面不太一樣
因為我這系列文章是四年前寫的了
所以可能東西已經變化不少了~~~
你是因為光碟丟了?
還是沒有買書呀?
板大您好,我想請問一下有關於伺服馬達的問題
回覆刪除在書上說明的週期為20~30ms,脈波寬度為0.8~2.2ms
如果用us為單位的話,會動作嗎? 會對馬達造成傷害嗎?
2者的差異在哪裡呢?
servo的種類很多
刪除常見的RC servo必須送連續信號給他
如果沒有連續送就會自動放棄鎖角度
這個週期就是你說的20~30mS
控制角度就是0.8~2.2mS
對應的角度一般是-60~+60度
中高階的會到-90~+90度
特別一點的有-110~+110度及360度連續運轉
不過控制信號大多落在0.5mS~2.5mS之間
如果你用的是uS為單位
其實就是800uS~2200uS
這樣的條件是相等的
但是如果你送0.8uS~2.2uS
那馬達收到這樣的指令是看不懂的
所以會怎麼動作並不曉得
有些馬達會利用<0.5mS的時間當做設定參數 (很高階的馬達)
如果你的信號剛好落在這個範圍
可能會不小心把出廠設定改掉
大概是這樣囉~~~
板大 伺服機訊號我送進週期25ms 脈波寬度1ms
回覆刪除訊號從pic經過741放大 從5v-- 7v
供給電壓6v
週期從0.8~2.2ms都調整過 都沒動靜..
用手去碰轉軸它會前後轉動 轉動幅度沒有很大 不碰就甚麼動作都沒有..
伺服機的地與741共用 這樣會有影響嗎?
還是說這顆已經陣亡了呢??
週期如果是0.8~2.2mS
刪除那一定動不了
如果你的週期25mS, 脈寬1mS連續送40次(約一秒)
再送週期25mS, 脈寬2mS連續送40次(約一秒)
這樣看看能不能動
再來為什麼要放大信號?
servo的輸入信號通常是TTL準位
所以2V以上就算High
0.8V以下就算Low
信號應該不用經過放大
放大信號準位反而會讓servo死掉
(一般IC最多承受到6V就不行了)
電源的電壓在servo內部會經過降壓才到IC
可是信號應該是直接接到IC上的
你對servo的認識可能有點錯誤唷~~~
再試試看吧
如果不能動應該是控制板被過高的電壓打壞了
那就得換一個servo了......
板大 想請問一下 如果要讓馬達連續動作 左轉到底--中立--右轉到底 每個動作都隔一秒 這樣子循環的話
回覆刪除那我要給馬達的信號是 0.8mS的1秒--1.5mS的1秒--2.2mS的1秒--1.5mS的1秒--0.8mS的1秒
還是 0.8mS1個--delay1秒--1.5mS1個--delay1秒--2.2mS1個--delay1秒--1.5mS1個
不太了解書上的意思
servo每20mS就一定要有一個信號給他
刪除如果delay了1秒才送
那剩下的980mS都沒有信號
servo應該是完全不會動才對
正確的作法是
0.8mS的重覆送50次 (20x50=1000mS=1S)
1.5mS的再重覆送50次
2.2mS的接著送50次
以此類推
這樣才是正確的servo信號
不好意思回覆得較晚
希望這樣說明有解決您的問題
板大 目前手臂動作都有了可是會有奇怪的抖動 該如何解決馬達抖動問題呢?
回覆刪除是硬體組裝上的誤差?? 還是軟體有問題呢??
另外 一顆馬達配一個訊號嗎? 可以兩顆馬達一個訊號嗎?
會抖動有很多原因
刪除如果是馬達本體的問題
靜止時就會抖動
那要克服就很難
只能換馬達
如果是運動時才會抖動
那就要看看哪個關節的結構需要加強
或是運動的速度需要調整
很可能是運動慣量超過馬達的負載所造成的抖動
一顆馬達原則上只能一個控制信號
即便是同步的信號在同廠牌的馬達上
還是會有些許的角度誤差
這個誤差會讓本來應該同步互相分擔負載的馬達
變成互相消力加重負載
如果不是同軸且負載不重就沒差
我的demo影片其中一個就是一個控制信號控制4~5個servo
不過每次的移動量都很小