/***************************************************************************************************
EME(月面反射通訊)/AMAST(業餘衛星通信)用控制器 (核心碼)
利用 控制轉向設備(定向天線) 自動/手動 達成 EME / AMAST 時透過軟件追逐目標之功能
(PCB線路圖,程式檔案在頁尾)
主核心: PIC16F877A
使用週邊: GY-26 (電子羅盤0~360)[0~3600 通訊]
SCA60C N1000060 單軸傾角傳感器
(傾角器-90~+90)[0.5~4.5 VDC ] 為了要能偵測超過下限,下限建議至少定在0.8~1左右
MAX232 (與上位機通訊)
1602 (液晶顯示)
液晶顯示: 第一行 顯示 "方位角 傾角 自動A/手動M 模式"
第二行 顯示 "目前狀態" 左/右/上/下 旋轉"
支援指令: GS-232A (部份指令)
C //要求 水平角度
B //要求 傾角
C2 //要求 水平 + 傾角
M //指令 前往 Mxxx 水平角度xxx
W //指令 前往 Wxxx yyy 水平角度xxx 傾角yyy
S //指令 All Stop
R //指令 Clockwise Rotation
L //指令 Counter Clockwise Rotation
A //指令 CW/CCW Rotation Stop
U //指令 UP Direction Rotation
D //指令 DOWN Direction Rotation
E //指令 UP/DOWN Direction Rotation Stop
I/O定義:
定義 TRISx 1為輸入/ 0為輸出 / x 為未始用(1) (TRISA只有6個Bit)
TRISA x x 0 x 0 0 0 1 <--- Bit0:傾角電壓輸入 /Bit1:手[自]動(接地=手動) /Bit235:上下左
TRISB 0 0 0 0 0 0 0 0 <--- LCD IO 輸出
TRISC 1 0 0 0 0 0 0 0 <--- Bit01: 停右 /Bit2:PWM輸出/Bit34:I2C總線/Bit6:RS-232TX輸出/Bit7:RS-232RX輸入
TRISD 0 0 0 0 0 0 0 0 <--- Bit01267:R.L.OUTPUT / Bit345:LCD command
定義 PORTx 1為高電平/ 0為低電平 / x 為未始用
(TRISA只有6個Bit)電壓輸入口設低電平/其他需要改變狀態(拉成低電平)的設成高電平/再其他亦可都設成高電平
PORTA x x 1 1 1 1 1 0 <--- Bit0:傾角電壓輸入 /Bit1:手[自]動(接地=手動) /Bit235:上下左輸入
PORTB 1 1 1 1 1 1 1 1 <--- LCD IO 輸出
PORTC 1 0 0 0 0 0 0 0 <--- Bit01:停右輸入/Bit2:PWM輸出/Bit34:I2C總線/Bit6:RS-232TX輸出/Bit7:RS-232RX輸入
PORTD 1 1 1 1 1 1 1 1 <--- Bit01267:R.L.OUTPUT (高電平 = 1 = 不動作 )/ Bit345:LCD command
接線注意 : 控制雲台轉向的繼電器實際接線時,
將110V電源其中一線接到 上(COM) 再將 上(NC)->下(COM) 以此類推 ->左-->右
這樣可以防止萬一繼電器同時動作時,110V的控制電源同時輸出(雖然程式有保護了,還是要有硬線防呆!)
PS.控制線接在NO
維護記錄: 2012-07-20
***************************************************************************************************/
#include <pic.h> //頭文件在HI-TECH安裝目錄下\HI-TECH Software\PICC\std\9.60\include
#include <stdio.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int
#define GY_26_ADDR 0xE0 //定義電子指南針器件地址(出廠默認為0XE0)
#define rs RD5 //LED 命令/數據選擇
#define rw RD4 //LED 讀寫口
#define e RD3 //LED 鎖存控制
#define Dataport PORTB //LED 數據口定義
void delay_20us(uint nus);
void mcu_init(void);
void nms_delay(uint nms);
uint get_GY_26(uchar read_begin_address,uchar GY_26_cmd);
uint get_AD(void);
void conversion(uint i);
void cmdIU(uint i);
//iic使用函數********************************//
void iic_Start(void){SEN=1;while(SEN);} //啟動iic
void iic_Stop (void){PEN=1;while(PEN);} //停止iic
void iic_rest (void){RSEN=1;while(RSEN);} //重啟iic
void iic_Write8Bit(uchar x){SSPIF=0;SSPBUF=x;while(!SSPIF);SSPIF=0;}//向iic總線寫8bit數據
uchar iic_read8Bit(void){RCEN=1;while(!BF);ACKEN=1;while(ACKEN);return SSPBUF;}//向iic總線讀8bit數據
//****LCD定位點*********012345678901234567***************//
bank2 uchar dis1[18]={"---- BV4WD -----\0"}; //顯示兩行
bank2 uchar dis2[18]={"-- Waitting ! --\0"}; //開機預設的顯示畫面,等待雲臺離開死點後進入程式會刷新
bank2 unsigned int CMP_DATA;
bank2 unsigned int result;
bank3 uchar disAZ[14]={"+00000000000\0"}; //水平
bank3 uchar disEL[14]={"+00000000000\0"}; //傾角
bank3 uchar disAZEL[14]={"+0000+000000\0"}; //水平 + 傾角
bank1 uint AZ; // 檢測到的目前 水平角度
bank1 uint EL; // 檢測到的目前 傾角
bank1 uint cmd_AZ; // 指令 至 水平角度
bank1 uint cmd_EL; // 指令 至 傾角
//**************************************以下僅做旗標用***********//
bank1 int rd_AZ; //要求 水平角度
bank1 int rd_EL; //要求 傾角
bank1 int rd_AZEL; //要求 水平 + 傾角
bank1 int rd_M; //指令 前往 Mxxx 水平角度xxx
bank1 int rd_W; //指令 前往 Wxxx yyy 水平角度xxx 傾角yyy
bank1 int rd_S; //指令 All Stop
bank1 int rd_R; //指令 Clockwise Rotation
bank1 int rd_L; //指令 Counter Clockwise Rotation
bank1 int rd_A; //指令 CW/CCW Rotation Stop
bank1 int rd_U; //指令 UP Direction Rotation
bank1 int rd_D; //指令 DOWN Direction Rotation
bank1 int rd_E; //指令 UP/DOWN Direction Rotation Stop
//**************************************以上僅做旗標用***********//
//***********以下變數使用 BANK1 *****************************
bank1 int RecTemp; //rs-232資料讀入暫存
bank1 uchar RecArray[12]; //將 RS-232 命令 寫入EEPROM用字串介面
//*********************************************
//數據轉換,十六進制數據轉換成10進制
//輸入十六進制範圍:0x0000-0x270f(0-9999)
//結果分成個十百千位,以ascii存入顯示區
//**********************************************
void conversion(unsigned int i)
{
unsigned int j;
j=i; //將j=i 使用 j 做PWM運算
AZ=i/10;
/* 不需羅盤0~360度 轉 PWM 0~5vdc 時, 本區關閉 節省資源
PR2 = 0b10111110; //經實測 PR2/T2CON/CCP1CON 使用固定數值
T2CON = 0b00000100; //CCPR1L從 0~190 大約可對應 0~5vDC輸出
CCP1CON = 0b00111100; //j=0~3600 3600/19 =189(取整數)
CCPR1L =(j/19)+1; //189+1=190 ;+1之後表示 0度~2度會有 "1"
//所以精度大約為2度(後面定位時會可採用+/-2度及算定位完成)
*/
dis1[3]=i/1000+0x30; // i : 取自電子羅盤的角度
i=i%1000; //取餘數運算
dis1[4]=i/100+0x30;
i=i%100; //取餘數運算
dis1[5]=i/10+0x30;
i=i%10; //取餘數運算
dis1[6]=0xDF; //(度)
//*************************************************************
//這是電壓顯示部份,不顯示 不需運算
//*************************************************************
/*顯示 羅盤0~360度 轉 PWM 0~5vdc 時,的理論電壓值
j=(((j/19)+1)*100)/38; // j : 計算出來的理論電壓
dis1[10]=j/100+0x30;
j=j%100; //取餘數運算
dis1[11]=0x2E; //.(小數點)
dis1[12]=j/10+0x30;
j=j%10; //取餘數運算
dis1[13]=j+0x30;
dis1[14]=0x30;
dis1[15]=0x56; // V
//以下是 計算/顯示 至 小數位 但無意義
dis1[3]=i/1000+0x30; // i : 取自電子羅盤的角度
i=i%1000; //取餘數運算
dis1[4]=i/100+0x30;
i=i%100; //取餘數運算
dis1[5]=i/10+0x30;
i=i%10; //取餘數運算
dis1[6]=0x2E; //.(小數點)
dis1[7]=i+0x30;
dis1[8]=0xDF; //(度)
*/
//*****************************************
//將水平 度數值放入暫存區等候RS-232 調用
//*****************************************
disAZ[2]=dis1[3]; //水平角度 百位
disAZ[3]=dis1[4]; //水平角度 十位
disAZ[4]=dis1[5]; //水平角度 個位
//*****************************************
//將水平 + 傾角 角度放入暫存區等候RS-232 調用
//*****************************************
disAZEL[2]=dis1[3]; //水平角度 百位
disAZEL[3]=dis1[4]; //水平角度 十位
disAZEL[4]=dis1[5]; //水平角度 個位
}
//**************************************************************************************************
//數據轉換,AD轉換結果
//輸入十六進制範圍:0x0000-0x03FF(0-1023)
//結果分成個十百千位,以ascii存入顯示區
//**************************************************************************************************
void conversion2(unsigned int d)
{
int ad1,ad2,ad3,ad4; //定義6個AD轉換臨時變量 整數常數 int 佔有 16 個 位元
//float temp,temp1; //定義4個計算臨時變量 浮點常數 float 佔有 32 個 位元
float temp,temp1,ed1,ed2; //定義4個計算臨時變量 浮點常數 float 佔有 32 個 位元
temp=d*5.0/1023; //暫存AD轉換的結果 10位AD 2的10次方=1024
temp1=temp; //將數值暫存 temp 計算電壓 / temp1 計算角度
ad1=(int)temp; //計算輸入電壓
ad2=((int)(temp*10)-ad1*10);
ad3=((int)(temp*100)-ad1*100-ad2*10);
ad4=((int)(temp*1000)-ad1*1000-ad2*100-ad3*10);
//*************************************************************
//這是電壓顯示部份,不顯示 不需運算
//*************************************************************
//dis2[10]=(int)temp+0x30; //顯示 個位
//dis2[11]=0x2E; //.(小數點)
//dis2[12]=((int)(temp*10)-ad1*10)+0x30; //顯示 小數第一位
//dis2[13]=((int)(temp*100)-ad1*100-ad2*10)+0x30; //顯示 小數第二位
//dis2[14]=((int)(temp*1000)-ad1*1000-ad2*100-ad3*10)+0x30; //顯示 小數第三位
//dis2[15]=0x56; // V
//EEPROM_WRITE(0x08,0x01);
//EEPROM_WRITE(0x09,0x04);
//********************************************************************************
//(傾角器-90~+90[共180])[0.5~4.5 VDC ] 為了要能偵測超過下限,下限建議至少定在高於0.8v
//4.5-0.5=4 4/2=2 (VDC)<--只取中間90度用 下限取1.5v/上限取1.5+2=3.5V/中點2.5v
//********************************************************************************
ed1=1.5; //電壓下限 0度
ed2=3.5; //電壓上限 90度
//*************************************************************
temp1=(temp1-ed1)*(90.0/(ed2-ed1)); //計算角度 0~90
EL=(int)temp1;
ad1=((int)(temp1/10)); //十
ad2=((int)(temp1-ad1*10)); //個
ad3=((int)(temp1*100)-ad1*10-ad2); //小數
dis1[11]=((int)(temp1/10))+0x30; //顯示 十位
dis1[12]=((int)(temp1-ad1*10))+0x30; //顯示 個位
dis1[13]=0xDF; //(度)
//*************************************************************
//可計算/顯示 至 小數位 但無意義
//*************************************************************
//dis2[6]=0x2E; //.(小數點)
//dis2[7]=((int)(temp1*100)-ad1*10-ad2)+0x30; //顯示 小數第一位
//*****************************************
//將傾角 度數值放入暫存區等候RS-232 調用
//*****************************************
disEL[2]=0x30; //水平角度 百位
disEL[3]=dis1[11]; //水平角度 十位
disEL[4]=dis1[12]; //水平角度 個位
//*****************************************
//將 傾角 角度放入暫存區等候RS-232 調用
//*****************************************
disAZEL[7]=0x30; //傾角角度 百位
disAZEL[8]=dis1[11]; //傾角角度 十位
disAZEL[9]=dis1[12]; //傾角角度 個位
if(temp<ed1) //當輸入電壓低於 電壓下限
{
disEL[2]=0x30; //傾角角度 百位 設0
disEL[3]=0x30; //傾角角度 十位 設0
disEL[4]=0x30; //傾角角度 個位 設0
disAZEL[7]=0x30; //水平+傾角的 傾角角度 百位 設0
disAZEL[8]=0x30; //水平+傾角的 傾角角度 十位 設0
disAZEL[9]=0x30; //水平+傾角的 傾角角度 個位 設0
dis1[11]='L';
dis1[12]='o';
}
if(temp>ed2) //當輸入電壓高於 電壓上限
{
disEL[2]=0x30; //傾角角度 百位 設0
disEL[3]=0x39; //傾角角度 十位 設9
disEL[4]=0x30; //傾角角度 個位 設0
disAZEL[7]=0x30; //水平+傾角的 傾角角度 百位 設0
disAZEL[8]=0x39; //水平+傾角的 傾角角度 十位 設9
disAZEL[9]=0x30; //水平+傾角的 傾角角度 個位 設0
dis1[11]='O';
dis1[12]='v';
}
}
//**************************************************************************************************
//數據轉換,ascii 轉換成 十六進制數據
//**************************************************************************************************
void con_WB(void)
{
unsigned int k,p;
k=0;
p=0;
k=(RecArray[1]-0x30)*100;
k=k+(RecArray[2]-0x30)*10;
k=k+(RecArray[3]-0x30);
if(RecArray[0]=='M'|RecArray[0]=='W')
{
cmd_AZ=(int)k;
}
/*
if(k>=0xFF) //寫入 EEPROM 需耗費時間較長 不需要時不寫入
{
k=k-0xff; //大於 256
EEPROM_WRITE(0x00,0x01); //寫入 水平數值 高位數 至EEPROM 位址0x00
EEPROM_WRITE(0x01,k); //寫入 水平數值 低位數 至EEPROM 位址0x01
}
else
{
EEPROM_WRITE(0x00,0x00); //寫入 水平數值 高位數 至EEPROM 位址0x00
EEPROM_WRITE(0x01,k); //寫入 水平數值 低位數 至EEPROM 位址0x01
}
*/
if(rd_W==1)
{
p=(RecArray[5]-0x30)*100;
p=p+(RecArray[6]-0x30)*10;
p=p+(RecArray[7]-0x30);
if(RecArray[0]=='W')
{
cmd_EL=(int)p;
}
/*
if(p>=0xFF) //寫入 EEPROM 需耗費時間較長 不需要時不寫入
{
p=p-0xff; //大於 256
EEPROM_WRITE(0x03,0x01); //寫入 傾角數值 高位數 至EEPROM 位址0x03
EEPROM_WRITE(0x04,p); //寫入 傾角數值 低位數 至EEPROM 位址0x04
}
else
{
EEPROM_WRITE(0x03,0x00); //寫入 傾角數值 高位數 至EEPROM 位址0x03
EEPROM_WRITE(0x04,p); //寫入 傾角數值 低位數 至EEPROM 位址0x04
}
*/
}
}
//**************************************************************************************************
//單片機初始化
//**************************************************************************************************
void mcu_init(void)
{
//TRISC=0x80; //IIC通信引腳設為輸入
SSPSTAT = 0x80; //IIC設置
SSPCON = 0x38; //IIC設置
SSPCON2 = 0x00; //IIC設置
SSPADD = 50; //IIC設置
}
//**************************************************************************************************
//延時大約20us
//**************************************************************************************************
void delay_20us(uint nus)
{ uchar i;
while(--nus){
for(i=2;i>0;i--);
}
}
//**************************************************************************************************
//延時大約1ms
//**************************************************************************************************
void nms_delay(uint nms)
{
while(nms--)
delay_20us(50);
}
//**************************************************************************************************
//延時
//**************************************************************************************************
void delay(uint time)
{
uint i,j;
for(i = 0;i < time; i++);
{
for(j = 0;j <30; j++);
}
}
//**************************************************************************************************
//從IIC總線獲得電子指南針GY-26數據
//從開始地址連續讀出兩個8bit的值,併合成16bit數據
//**************************************************************************************************
uint get_GY_26(uchar read_begin_address,uchar GY_26_cmd)
{
unsigned int temp;
iic_Start(); //iic啟動時序
iic_Write8Bit(GY_26_ADDR); //寫指南針器件地址
iic_Write8Bit(0); //寫命令
iic_Write8Bit(GY_26_cmd); //寫指南針命令
iic_Stop(); //iic寫停止時序
nms_delay(60); //延時,可執行其他程序 原始值:55
iic_Start(); //iic啟動時序
ACKDT = 0; //允許應答ACK
//*******************************************************************************
//需修正磁偏角時使用「台灣磁偏角目前試定為3.5度」
//第一次使用時最好修正,因為GY-26大部分修正偏角是6度(或10度)
//*******************************************************************************
//iic_Write8Bit(0x03); //發送設定磁偏角高8位命令。
//iic_Write8Bit(0x00); //發送磁偏角高8位的數據。
//iic_Write8Bit(0x03); //發送設定磁偏角低8位命令
//iic_Write8Bit(0x23); //發送磁偏角低8位的數據。此時磁偏角被設定為3.5度
iic_Write8Bit(GY_26_ADDR); //寫指南針器件地址
iic_Write8Bit(read_begin_address); //寫指南針內部數據地址
iic_rest(); //iic重啟時序
iic_Write8Bit(GY_26_ADDR+1); //指南針器件地址+1=讀
temp=iic_read8Bit(); //讀出8位數據
temp<<=8; //保存數據
ACKDT = 1; //允許應答NO_ACK
temp+=iic_read8Bit(); //讀出8位數據
iic_Stop(); //發送停止時序
return temp; //返回本次測量數值
}
//**************************************************************************************************
//從AN0讀取轉換電壓的相對值
//從ADRESH ADRESL 讀出兩個8bit的值,併合成16bit數據
//**************************************************************************************************
uint get_AD()
{
unsigned int temp;
result=0; //轉換結果清0
ADGO=0x01; //開啟轉換過程
delay(50); //保證採樣延時
//ADGO=0x01; ////ADGO=1放在此採樣不行
while(ADGO); //等待轉換完成
temp=ADRESH; //讀出8位數據
temp<<=8; //保存數據
temp+=ADRESL; //讀出8位數據
return temp; //返回本次測量數值
}
//**************************************************************************************************
//IO初始化操作
//定義 TRISx 1為輸入/ 0為輸出 / x 為未始用(1) (TRISA只有6個Bit)
// TRISA x x 0 x 0 0 0 1 <--- Bit0:傾角電壓輸入 /Bit1:手[自]動(接地=手動) /Bit235:上下左
// TRISB 0 0 0 0 0 0 0 0 <--- LCD IO 輸出
// TRISC 1 0 0 0 0 0 0 0 <--- Bit01: 停右 /Bit2:PWM輸出/Bit34:I2C總線/Bit6:RS-232TX輸出/Bit7:RS-232RX輸入
// TRISD 0 0 0 0 0 0 0 0 <--- Bit01267:R.L.OUTPUT / Bit345:LCD command
//
//定義 PORTx 1為高電平/ 0為低電平 / x 為未始用
// (TRISA只有6個Bit)電壓輸入口設低電平/其他需要改變狀態(拉成低電平)的設成高電平/再其他亦可都設成高電平
// PORTA x x 1 1 1 1 1 0 <--- Bit0:傾角電壓輸入 /Bit1:手[自]動(接地=手動) /Bit235:上下左輸入
// PORTB 1 1 1 1 1 1 1 1 <--- LCD IO 輸出
// PORTC 1 0 0 0 0 0 0 0 <--- Bit01:停右輸入/Bit2:PWM輸出/Bit34:I2C總線/Bit6:RS-232TX輸出/Bit7:RS-232RX輸入
// PORTD 1 1 1 1 1 1 1 1 <--- Bit01267:R.L.OUTPUT (高電平 = 1 = 不動作 )/ Bit345:LCD command
//**************************************************************************************************
void IO_init(void)
{
TRISA=0x01; //RA0設置為輸入 00 0001
PORTA=0x3e; //設置RA0為低電平
TRISC=0x80; //RC7設置為輸入 1000 0000
PORTC=0xff; //初始化為高電平
TRISB=0x00; //RB設置為輸出 LCD IO
PORTB=0xff; //初始化為高 LCD IO 為高電平
TRISD=0x00; //RD設置為輸出 LCD command
PORTD=0b11111111; //初始化 LCD command(Bit345) 為高電平 /R.L.OUTPUT為高電平
ADCON1=0X8E; //1000 1110 A/D 轉換用參數 需查表 [PIC16F87XA單片機說明書.pdf p.129~]
ADCON0=0X41; //0100 0001 A/D 轉換用參數 需查表 [PIC16F87XA單片機說明書.pdf p.128~]
}
//**************************************************************************************************
//雲臺初始化操作 (離開限位點)
//向上轉3秒 向下轉2秒
//向左轉3秒 向右轉2秒
//**************************************************************************************************
void Init_wt(void)
{
/*
RD0=0x00;
RD1=0x00;
RD6=0x00;
RD7=0x00;
nms_delay(1000);
RD2=0x01; //RD2 失能(亮)
nms_delay(1000); //可將訊號接至 雲臺旁 閃爍紅燈警示
RD2=0x00; //RD2 致能(確認完成開機,控制雲臺電源可輸出[實驗板LED亮->熄滅])
nms_delay(1000); //等待 進入旋轉初始化
RD0=0x01; //指令 UP Direction Rotation
RD6=0x01; //指令 Counter Clockwise Rotation
nms_delay(3000);
RD0=0x00; //指令 UP Direction Rotation
RD6=0x00; //指令 Counter Clockwise Rotation
RD0=0x00; //指令 UP Direction Rotation
nms_delay(500);
RD1=0x01; //指令 DOWN Direction Rotation
RD7=0x01; //指令 Clockwise Rotation
nms_delay(3000);
RD1=0x00; //指令 DOWN Direction Rotation
RD7=0x00; //指令 Clockwise Rotation
nms_delay(500);
*/
nms_delay(1000); //等待
//*******************************************************************************
//完成轉動後重新將LCD內容刷新
//*******************************************************************************
dis1[0]='A';
dis1[1]='Z';
dis1[2]=':';
dis1[3]='0';
dis1[4]='0';
dis1[5]='0';
dis1[6]=' ';
dis1[7]=' ';
dis1[8]='E';
dis1[9]='L';
dis1[10]=':';
dis1[11]='0';
dis1[12]='0';
dis1[13]=' ';
dis1[14]=' ';
dis1[15]=' ';
dis2[0]='-';
dis2[1]='-';
dis2[2]='-';
dis2[3]='-';
dis2[4]='-';
dis2[5]='-';
dis2[6]='-';
dis2[7]='-';
dis2[8]='-';
dis2[9]='-';
dis2[10]='-';
dis2[11]='-';
dis2[12]='-';
dis2[13]='-';
dis2[14]='-';
dis2[15]='-';
}
//**************************************************************************************************
//向LCD寫一命令
//**************************************************************************************************
wcode(uchar t)
{
rs=0; // 寫的是命令
rw=0; // 寫狀態
e=1; //使能
Dataport=t; //寫入命令
delay(45); //等待寫入,如果時間太短,會導致液晶無法顯示
e=0; //數據的鎖定
}
//**************************************************************************************************
//向LCD寫一數據
//**************************************************************************************************
wdata(uchar t)
{
rs=1; // 寫的是數據
rw=0; // 寫狀態
e=1; //使能
Dataport=t; //寫入數據
delay(45); //等待寫入,如果時間太短,會導致液晶無法顯示
e=0; //數據的鎖定
}
//**************************************************************************************************
//LCD顯示第一行
//**************************************************************************************************
xian1(void)
{
uchar i;
wcode(0x80); //設置第一行顯示地址
for(i=0;i<16;i++) //循環16次,寫完1行
{
wdata(dis1[i]); //寫入該行數據
}
}
//**************************************************************************************************
//LCD顯示第二行
//**************************************************************************************************
xian2(void)
{
uchar i;
wcode(0xc0); //設置第二行顯示地址
for(i=0;i<16;i++) //循環16次,寫完1行
{
wdata(dis2[i]); //寫入該行數據
}
}
//**************************************************************************************************
//LCD 初始化
//**************************************************************************************************
InitLCD(void)
{
wcode(0x01); //清屏
wcode(0x06); //輸入方式控制,增量光標不移位
wcode(0x0e); //顯示開關控制
wcode(0x38); //功能設定:設置16x2顯示,5x7顯示,8位數據接口
}
//**************************************************************************************************
//初始化串口
//**************************************************************************************************
void init_com( void )
{
TXSTA=0x24;//發送數據控制位配置00100100,異步方式、高波特率,波特率=Fosc/16*(SPBTG+1);
//TXSTA寄存器的D5(TXEN)=1,啟動UART的發送功能
RCSTA=0x90;//接收數據控制位配置10010000,設置連續接收數據
//RCSTA寄存器的D4(CREN)=1,啟動UART的接收功能
SPBRG=25; //設置波特率9600,系統時鐘4MHZ,波特率=4000000/16/(25+1)=9615(Bps) 51
//設置波特率4800,系統時鐘4MHZ,波特率=4000000/16/(51+1)=4807(Bps)
RCIE=1; //接收中斷使能
TXIE=0; //發送中斷使/失能
PEIE=1; //INTCON的D6(PEIE)=1,外圍中斷開關打開
RCIF=0;
RCREG=0;
GIE=1; //總中斷開啟
}
//**************************************************************************************************
//寫RS-232
//**************************************************************************************************
void WriteByte(unsigned char byte) {
while(!TXIF);
{
TXREG = byte;
}
}
//**************************************************************************************************
//向RS-232寫一字串數據
//**************************************************************************************************
void WriteStringConst(const unsigned char* str)
{
int n,m;
for(n=0;n<=11;n++)
{
//TXREG = str[n];
WriteByte(str[n]);
}
}
//**************************************************************************************************
//雲臺轉向控制
// 選用的雲臺 傾角 動作時會稍微干擾電子羅盤
// 雖然已經將 羅盤 與 馬達 距離拉開,但在做 傾角 動作時,還是先 不旋轉 以免因為誤判角度而亂轉
//**************************************************************************************************
conTIO(void)
{
RD0= !rd_U; //指令 UP Direction Rotation(反邏輯 單片機輸出"0"=低電平=繼電器動作)
RD1= !rd_D; //指令 DOWN Direction Rotation(反邏輯 單片機輸出"0"=低電平=繼電器動作)
if(rd_U==0&&rd_D==0) //傾角動作優先,傾角動作完成之後再旋轉
{
RD6= !rd_L; //指令 Counter Clockwise Rotation(反邏輯 單片機輸出"0"=低電平=繼電器動作)
RD7= !rd_R; //指令 Clockwise Rotation(反邏輯 單片機輸出"0"=低電平=繼電器動作)
}
}
//**************************************************************************************************
//check 資料要求 COMMAND
//
//int rd_AZ; //要求 水平角度
//int rd_EL; //要求 傾角
//int rd_AZEL; //要求 水平 + 傾角
//**************************************************************************************************
cmdCHK(void)
{
if(rd_AZ==1)
{
WriteStringConst(disAZ);
rd_AZ=0;
}
if(rd_EL==1)
{
WriteStringConst(disEL);
rd_EL=0;
}
if(rd_AZEL==1)
{
WriteStringConst(disAZEL);
rd_AZEL=0;
}
}
//**************************************************************************************************
//雲臺自動轉向 COMMAND
//
//int rd_M; //自動指令 前往 Mxxx 水平角度xxx
//int rd_W; //自動指令 前往 Wxxx yyy 水平角度xxx 傾角yyy
//int rd_S; //指令 All Stop
//int rd_R; //指令 Clockwise Rotation
//int rd_L; //指令 Counter Clockwise Rotation
//int rd_A; //指令 CW/CCW Rotation Stop
//int rd_U; //指令 UP Direction Rotation
//int rd_D; //指令 DOWN Direction Rotation
//int rd_E; //指令 UP/DOWN Direction Rotation Stop
//**************************************************************************************************
void turn_cmd(void)
{
//if(cmd_AZ>AZ&&(cmd_AZ-AZ)>3 &&(cmd_AZ-AZ)<30)
if(cmd_AZ>AZ&&(cmd_AZ-AZ)>3)
{
nms_delay(100); //防止繼電器高速跳動
rd_R=1;
rd_L=0;
}
//if(AZ>cmd_AZ&&(AZ-cmd_AZ)>3&&(AZ-cmd_AZ)<30)
if(AZ>cmd_AZ&&(AZ-cmd_AZ)>3)
{
nms_delay(100); //防止繼電器高速跳動
rd_R=0;
rd_L=1;
}
//if(AZ >345 && cmd_AZ <15 ) // tracking cross left 過 360'
//{
// rd_R=1;
//}
//if(AZ <15 && cmd_AZ >345 ) // tracking cross right 過 360'
//{
// rd_L=1;
//}
if(cmd_AZ>AZ&&(cmd_AZ-AZ)<3)
{
rd_R=0;
}
if(AZ>cmd_AZ&&(AZ-cmd_AZ)<3)
{
rd_L=0;
}
if(rd_R==0&&rd_L==0)
{
rd_M=0;
}
if(rd_W==1)
{
//if(cmd_EL>EL&&(cmd_EL-EL)>2&&(cmd_EL-EL)<20&&EL<90)
if(cmd_EL>EL&&(cmd_EL-EL)>2&&EL<90)
{
nms_delay(100); //防止繼電器高速跳動
rd_U=1;
rd_D=0;
}
//if(EL>cmd_EL&&(EL-cmd_EL)>2&&(EL-cmd_EL)>20&&EL>0)
if(EL>cmd_EL&&(EL-cmd_EL)>2&&EL>0)
{
nms_delay(100); //防止繼電器高速跳動
rd_D=1;
rd_U=0;
}
if(cmd_EL>EL&&(cmd_EL-EL)<2)
{
rd_U=0;
}
if(EL>cmd_EL&&(EL-cmd_EL)<2)
{
rd_D=0;
}
if(EL==0&&cmd_EL==0)
{
rd_D=0;
rd_U=0;
}
if(rd_R==0&&rd_L==0&&rd_U==0&&rd_D==0)
{
rd_W=0;
}
}
if(rd_A==1)
{
rd_R=0;
rd_L=0;
rd_A=0;
}
if(rd_E==1)
{
rd_D=0;
rd_U=0;
rd_E=0;
}
}
//**************************************************************************************************
//check 雲臺轉向 指示 及 過頭.回一點 控制
//
//int rd_M; //自動指令 前往 Mxxx 水平角度xxx
//int rd_W; //自動指令 前往 Wxxx yyy 水平角度xxx 傾角yyy
//int rd_S; //指令 All Stop
//int rd_R; //指令 Clockwise Rotation
//int rd_L; //指令 Counter Clockwise Rotation
//int rd_A; //指令 CW/CCW Rotation Stop
//int rd_U; //指令 UP Direction Rotation
//int rd_D; //指令 DOWN Direction Rotation
//int rd_E; //指令 UP/DOWN Direction Rotation Stop
//**************************************************************************************************
void turn_dsp(void)
{
if(dis1[11]=='L'&&dis1[12]=='o'&&result>>0) //指令 傾角過低 low(回頭)(當 傾角 輸入=0V(將該腳位接地) 時 不做「過頭回一點」的控制)
{
RD1=!0x00;
RD0=!0x01;
nms_delay(300);
RD1=!0x00;
RD0=!0x00;
}
if(dis1[11]=='O'&&dis1[12]=='v'&&result>>0) //指令 傾角過高 over(回頭)(當 傾角 輸入=0V(將該腳位接地) 時 不做「過頭回一點」的控制)
{
RD0=!0x00;
RD1=!0x01;
nms_delay(300);
RD1=!0x00;
RD0=!0x00;
}
//******012345678901234567***************//
//dis2{"Turn! Right Down\0"};
// {"Turn! Left UP \0"};
// {"------STOP------\0"};
//***********************************//
if(rd_U==1|rd_D==1|rd_L==1|rd_R==1)
{
dis2[0]='T';
dis2[1]='u';
dis2[2]='r';
dis2[3]='n';
dis2[4]='!';
dis2[5]=0x20;
dis2[6]=0x20;;
dis2[7]=0x20;;
dis2[8]=0x20;;
dis2[9]=0x20;;
dis2[10]=0x20;
dis2[11]=0x20;;
dis2[12]=0x20;;
dis2[13]=0x20;;
dis2[14]=0x20;;
dis2[15]=0x20;;
}
if(rd_U==1)
{
dis2[12]=0x20;
dis2[13]='U';
dis2[14]='P';
dis2[15]=0x20;
}
if(rd_D==1)
{
dis2[12]='D';
dis2[13]='o';
dis2[14]='w';
dis2[15]='n';
}
if(rd_L==1)
{
dis2[6]='L';
dis2[7]='e';
dis2[8]='f';
dis2[9]='t';
dis2[10]=0x20;
}
if(rd_R==1)
{
dis2[6]='R';
dis2[7]='i';
dis2[8]='g';
dis2[9]='h';
dis2[10]='t';
}
if(rd_U==0&&rd_D==0)
{
dis2[12]=0x20;
dis2[13]=0x20;
dis2[14]=0x20;
dis2[15]=0x20;
}
if(rd_L==0&&rd_R==0)
{
dis2[6]=0x20;
dis2[7]=0x20;
dis2[8]=0x20;
dis2[9]=0x20;
dis2[10]=0x20;
}
if(rd_U==0&&rd_D==0&&rd_L==0&&rd_R==0)
{
dis2[0]='-';
dis2[1]='-';
dis2[2]='-';
dis2[3]='-';
dis2[4]='-';
dis2[5]='-';
dis2[6]='-';
dis2[7]='-';
dis2[8]='-';
dis2[9]='-';
dis2[10]='-';
dis2[11]='-';
dis2[12]='-';
dis2[13]='-';
dis2[14]='-';
dis2[15]='-';
}
}
//*********************************************
//雲臺 手動 轉向 COMMAND
//
//int rd_S; //指令 All Stop
//int rd_R; //指令 Clockwise Rotation
//int rd_L; //指令 Counter Clockwise Rotation
//int rd_A; //指令 CW/CCW Rotation Stop
//int rd_U; //指令 UP Direction Rotation
//int rd_D; //指令 DOWN Direction Rotation
//int rd_E; //指令 UP/DOWN Direction Rotation Stop
//**********************************************
void PB_cmd(void)
{
rd_M=0;
rd_W=0;
rd_L=0;
rd_R=0;
rd_A=0;
rd_U=0;
rd_D=0;
rd_E=0;
rd_S=0;
if(RA2==0) //檢查按鍵 上
{
nms_delay(5);
if(RA2==0)
{
rd_D=0; //互鎖
rd_U=1;
}
}
else
{
rd_U=0;
}
if(RA3==0) //檢查按鍵 下
{
nms_delay(5);
if(RA3==0)
{
rd_U=0; //互鎖
rd_D=1;
}
}
else
{
rd_D=0;
}
if(RC1==0) //檢查按鍵 左
{ // (RA4 不可以用(無法改變數值+狀態不定) /改用 RC1)
nms_delay(5);
if(RC1==0)
{
rd_R=0;
rd_L=1;
}
}
else
{
rd_L=0;
}
if(RA5==0) //檢查按鍵 右
{
nms_delay(5);
if(RA5==0)
{
rd_L=0;
rd_R=1;
}
}
else
{
rd_R=0;
}
if(RC0==0) //檢查按鍵 停
{
nms_delay(5);
if(RC0==0)
{
rd_L=0;
rd_R=0;
rd_U=0;
rd_D=0;
rd_S=1;
}
}
else
{
rd_S=0;
}
}
//*********************************************
//CHECK STOP COMMAND
//int rd_S; //指令 All Stop
//**********************************************
void CHK_stop(void)
{
if(RC0==0) //檢查按鍵 停
{
nms_delay(5);
if(RC0==0)
{
rd_L=0;
rd_R=0;
rd_U=0;
rd_D=0;
rd_S=1;
}
}
else
{
rd_S=0;
}
}
//**************************************************************************************************
//串口中斷函數
//接收要求或指令 並改變旗標
//int rd_AZ; //要求 水平角度
//int rd_EL; //要求 傾角
//int rd_AZEL; //要求 水平 + 傾角
//
//int rd_M; //指令 前往 Mxxx 水平角度xxx
//int rd_W; //指令 前往 Wxxx yyy 水平角度xxx 傾角yyy
//
//int rd_R; //指令 Clockwise Rotation
//int rd_L; //指令 Counter Clockwise Rotation
//int rd_A; //指令 CW/CCW Rotation Stop
//
//int rd_U; //指令 UP Direction Rotation
//int rd_D; //指令 DOWN Direction Rotation
//int rd_E; //指令 UP/DOWN Direction Rotation Stop
//
//int rd_S; //指令 All Stop
//**************************************************************************************************\
void interrupt usart(void)
{
int i;
if(RCIF)
{
RecArray[i] = RCREG;
i=i++;
//if(RCREG==0x0D|RCREG==0x20)
if(RCREG==0x0D)
{
//if(RecArray[0]==0x43&&rd_M!=1&&rd_W!=1) // 如果抓到開頭為 C 並且無自動指令 M W
if(RecArray[0]==0x43) // 如果抓到開頭為 C 並且無自動指令 M W
{
rd_AZ=1;
}
//if(RecArray[0]==0x42&&rd_M!=1&&rd_W!=1) // 如果抓到開頭為 B 並且無自動指令 M W
if(RecArray[0]==0x42) // 如果抓到開頭為 B 並且無自動指令 M W
{
rd_EL=1;
}
//if(RecArray[0]==0x43 && RecArray[1]==0x32&&rd_M!=1&&rd_W!=1) // 如果抓到開頭為 C2 並且無自動指令 M W
if(RecArray[0]==0x43 && RecArray[1]==0x32) // 如果抓到開頭為 C2 並且無自動指令 M W
{
rd_AZ=0;
rd_EL=0;
rd_AZEL=1;
}
if(RecArray[0]==0x4D) // 如果抓到開頭為 M 水平自動指令
{
rd_M=1;
}
if(RecArray[0]==0x57) // 如果抓到開頭為 W 水平+傾角自動指令
{
rd_W=1;
}
if(RecArray[0]==0x52) // 如果抓到開頭為 R
{
rd_L=0;
rd_R=1;
}
if(RecArray[0]==0x4C) // 如果抓到開頭為 L
{
rd_R=0;
rd_L=1;
}
if(RecArray[0]==0x41) // 如果抓到開頭為 A
{
rd_L=0;
rd_R=0;
rd_A=1;
}
if(RecArray[0]==0x55) // 如果抓到開頭為 U
{
rd_D=0;
rd_U=1;
}
if(RecArray[0]==0x44) // 如果抓到開頭為 D
{
rd_U=0;
rd_D=1;
}
if(RecArray[0]==0x45) // 如果抓到開頭為 E
{
rd_U=0;
rd_D=0;
rd_E=1;
}
if(RecArray[0]==0x53) // 如果抓到開頭為 S
{
rd_M=0;
rd_W=0;
rd_L=0;
rd_R=0;
rd_A=0;
rd_U=0;
rd_D=0;
rd_E=0;
rd_S=1;
}
i=0;
}
if(i>=11)
{
i=0; //如果超過12(0~11)個字元就把i歸零(一直重複,將此次訊號收完,防止溢位)
}
}
}
//**************************************************************************************************
//主函數
//**************************************************************************************************
main()
{
uchar cntl;
//__CONFIG(XT&WDTEN&LVPDIS);//配置,設置為晶振XT方式振蕩,看門狗,禁低電壓編程
__CONFIG(XT&WDTDIS&LVPDIS);//配置,設置為晶振XT方式振蕩,禁看門狗,禁低電壓編程
mcu_init(); //晶片初始化
IO_init(); //IO初始化
init_com(); //初始化串口
InitLCD(); //初始化1602
xian1(); //顯示 等待訊息
xian2(); //顯示 等待訊息
Init_wt(); //初始化雲臺(確保不會正好在 Over0 或是 Over360)
while(1) //進入while死循環
{
result=get_AD(); //讀取傾角電壓
conversion2(result); //數據轉換出個,十,百,千 位
if(rd_U==0&&rd_D==0)
{
CMP_DATA=get_GY_26(1,0x31); //讀取角度
conversion(CMP_DATA); //數據轉換出個,十,百,千 位
}
xian1(); //顯示 第1行刷新
xian2(); //顯示 第2行刷新
conTIO();
cmdCHK();
if(RA1==1) //檢查模式 自動=1 手動=0
{
dis1[15]='A';
if(rd_M==1|rd_W==1)
{
con_WB(); //轉換指令後之「文字」變成「數字」
turn_cmd(); //檢測是否要旋轉
//turn_dsp(); //旋轉狀態顯示
}
CHK_stop(); //檢測是否STOP有按下
turn_dsp(); //旋轉狀態顯示
}
if(RA1==0) //檢查模式 自動=1 手動=0
{
dis1[15]='M';
rd_M=0;
rd_W=0;
PB_cmd(); //掃描按鍵狀態
turn_dsp(); //旋轉狀態顯示
}
}
}