終於要開始寫通訊了
相信這一段是很多人心中的痛
並不是通訊連不上
而是連上了卻不正確動作
其中有很多設定的部分是要重覆確認的(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在這方面很講究
設錯了就不會動作
大致上就這樣囉~~~
下一篇再來看看如何動作的
hi nichal,
回覆刪除有個問題,大大的程式是用在晶片上,而且用通訊程式就可以連線控制伺服機的動作嗎?
還有,可不可下載程式來看看啦。因為不能按右鍵來複製啦。
這個部落格上的程式都是這樣囉。
是呀~~~
刪除只要這樣就可以動作了
夠簡單吧
原來我所用的PWM測試儀
就是這樣做的呀!!!
程式我可以直接丟給您
請問一下您的MSN是?
對了
我的程式只能用在F300唷
跟8051的原腳位不相容
所以要移植的話需要重新寫過
ps.
剛剛把程式寄到您學校的信箱了
開看看吧
你好
回覆刪除我現在想開發一個產品
我現在需要找位會寫程式,有MCU經驗和有打版能力的EE
如果有興趣配合的話麻煩你和我連絡
我的email是hello@amideas.com
謝謝
Helen Chen
Helen 您好
刪除請問您想開發哪一類的產品呢?
只要有幫得上忙的地方
基本上我都很樂意啦!!
請問
回覆刪除SERVO1_PWM;
的用意是甚麼? 因為我沒學過keil C所以看不太懂
只是一個定義
刪除在最上面有#define的設定
意思是第一組SERVO的PWM開始輸出