使用F320撰寫程式控制搖控車發射端PPM編碼輸出 系別:通訊三甲 學生:葉祥嵩B09322021 指導老師:田慶誠 教授
目錄 F320 Datasheet 設定說明 程式流程圖 PPM編碼演算法 程式 作品展示
F320 Datasheet 設定說明 10-bit ADC(ADC0) Port Input/Output Oscillators Timers Programmable Counter Array(PCA0) F320 Datasheet back
F320 Datasheet 設定說明 10-bit ADC(ADC0)
10-Bit ADC (ADC0) 由上圖我們大略可知道:(1) 有兩種輸入方式 (2) 需要控制的暫存器: AMX0P、AMX0N、 ADC0CN、ADC0CF (3) 經過 A/D 轉換後的資料存於 ADC0H、ADC0L
控制ADC0開始轉換 有六種方法可以控制。 我們採用 AD0BUSY (W) 當 AD0BUSY = 1 時 ADC0開始轉換 當 AD0BUSY = 0 時 代表 ADC0 轉換完成 或是未開始動作
A/D 輸入轉換模式: Single-Ended 和 Differential 此模式轉換為輸入 0(v) ~ VREF(v) 轉換得到 0 ~ 1023, 資料儲存方式可分為由最低位元開始排列或是由最高位元開始排列, 因為我們輸入電壓為 0(v) ~ 3.3(v) , 固使用這個模式, 而資料儲存方式採用從最低位元開始排列(AD0LJST=0)。 此模式轉換為輸入 -VREF(v) ~ VREF(v) 轉換得到 -511 ~ 512, 資料儲存方式可分為由最低位元開始排列或是由最高位元開始排列。
REF0CN:Reference Control Register Bit3:VDD used as voltage reference 因為我們電路的輸入電壓為0~3.3V 而IC內的VDD在標準時為3.3V 故我們將VDD當做是VREF
ADC0CF:ADC0 Configuration Register Bits7-3:設定 SAR clock = 31 , 所以設定為 “11111” Bit2:資料儲存方式為由最低位元開始排列或是由最高位元 開始排列 ,我們採用從最低位元開始排列(AD0LJST=0)
AMX0P:AMUX0 Positive Channel Select Register 此多工器可控制要輸入哪一個Pin腳做A/D轉換
AMX0N:AMUX0 Negative Channel Select Register 因為我們使用 Single-Ended Mode ,固 AMX0P = 0x1F
ADC0H 和 ADC0L 當 AD0LJST = 0 :資料由低位元開始排列,所以資料存於 ADC0H(Bits 1-0) 和 ADC0L(Bits 7-0) 共10-Bits 當 AD0LJST = 1 :資料由高位元開始排列,所以資料存於 ADC0H(Bits 7-0) 和 ADC0L(Bits 7-6) 共10-Bits
ADC0CN:ADC0 Control Register Bit7:ADC0 Enabled Bit6:Normal Track Mode Bits2-0:When AD0TM = 0 000:ADC0 conversion initiated on every write of '1' to AD0BUSY back
F320 Datasheet 設定說明 Port Input/Output
Port I/O Functional Block Diagram
Step 1. Select the input mode 要做 A/D 轉換的 input 必需設定為 analog input 我設定的輸入為 P1.2、P1.3、P1.4 固 P1MDIN = “11100011” = 0xE3
PnMDOUT 簡介 open-drain 電路圖 push-pull 電路圖 圖1 圖2 一般程式初始設定時,PnMDOUT會設定為open-drain, 然後因為我們輸出的PPM編碼週期時間短(1ms~2ms), 所以當輸出由 ‘0’ 變 ‘1’ 時,電壓沒辦法馬上從 0V 上升至 3.3V , 所以會波形會變形如圖1,所以我們要將輸出模式 設定為push-pull,這樣電壓供應源有兩個,所以就不會有電壓 無法馬上恢復的情況發生,正常PPM編碼如圖2。
使用示波器實際驗證 open-drain 波形圖 push-pull 波形圖
Step 2. Select the output mode PPM編碼輸出時,輸出必需設定為 push-pull 所以要輸出的腳位要設定為 ’1’
Step 3. Select any pins to be skipped by the Crossbar 要做 A/D 轉換的 analog input 必需關閉Digital crossbar 我們設定的輸入為 P1.2、P1.3、P1.4 固 P1SKIP = “00011100” = 0x1C
Step 4. Assign Port pins to desired peripherals Bit7:Weak Pull-ups enabled Bits5-0:我們並未使用, 所以選擇 unavailable Step 5. Enable the Crossbar Bit6:Crossbar enabled back
F320 Datasheet 設定說明 Oscillators
Oscillator Diagram 由右圖可知 需要控制的暫存器為: OSCICN、CLKSEL 、OSCXCN、 CLKMUL 因為我們最右邊的多工器選擇為Internal Oscillator輸入, 所以暫存器 OSCXCN、 CLKMUL可以不用設定, 因為並沒有使用所以不會影響
OSCICN:Internal Oscillator Control Register Bit7: Internal Oscillator Enabled Bits1-0: SYSCLK derived from Internal Oscillator divided by 1 代表輸入的Internal Oscillator經過一個除1的電路 所以Internal Oscillator / 1 = 12MHz
CLKSEL: Clock Select Register Bits1-0: Selected Clock from Internal Oscillator back
F320 Datasheet 設定說明 Timers
RSTRC: Reset Source Register Bit2: Missing Clock Detector (MCD) enabled
IE: Interrupt Enable 因為我們使用Timer0中斷所以要設定IE Bit7: Enable All Interrupt Bit1: Enable interrupt requests generated by the TF0 flag
TMOD: Timer Mode Register 我們使用 Timer1 Mode1 and Timer0 Mode1 Bits5-4: Mode 1: 16-bit counter/timer Bits1-0: Mode 1: 16-bit counter/timer back
F320 Datasheet 設定說明 Programmable Counter Array (PCA0)
PCA WDT 由Important Note我們可知 PCA WDT 在 system reset 後會自動 enabled,這樣會影響到我們原本的Timer1或Timer0的WDT,因此 我們要將此 distabled (我們並未使用PCA0), 控制它的暫存器是「PCA0MD」
PCA0MD: PCA Mode Register Bit6 : WDT disabled (clear watchdog timer enable IN ORDER TO CHANGE PCA0MD) back
主程式流程圖 Main 備註: 1.c8051f320初始化,表示啟動320最 基本條件。 2.將channel的類比電壓量化成0~1023 刻度。(ch1~ch3的AD轉換) 3.如下圖描述出PPM輸出訊號。 C8051f320初始化 CH1做AD轉換 CH2做AD轉換 CH3做AD轉換 輸出PPM訊號
= ADC0L + (ADC0H<<8) Ch1_AD轉換副程式 Ch1_AD ADC0CN = 0x80; RET P1MDIN = 0xFB; AD0BUSY = 1; P1SKIP = 0x04; while(AD0BUSY ) = 0? No AMX0P = 0x02; Yes ADC0CF = 0x08; ch1_adc_val = ADC0L + (ADC0H<<8) 備註:P1MDIN為P1.2 analog input
Timer0_Mode_1_Delay_low副程式 TIMER_Delay_low while(TF==0) ? Yes TMOD=0x01; No TH0=(65536-400)/256 TF=0; TL0=(65536-400)%256 TR0=0; TR0=1; RET 備註:計時為400us
Timer0_Mode_1_Delay_channel副程式 TIMER_Delay_Ch while(TF0==0) ? Yes TMOD=0x01; No TH0=(65536-C)/256 TF0=0; TL0=(65536-C)%256 TR0=0; TR0=1; RET
輸出PPM訊號副程式 PPM_Output PPM=1; PPM=0; PPM=1; Timer_Delay_low PPM=0; Timer_Delay_Ch3 PPM=1; Timer_Delay_low PPM=0; Timer_Delay_Ch2 PPM=1; Timer_Delay_low PPM=0; Timer_Delay_Ch1 PPM=1; Timer_Delay_low PPM=0; Timer_Delay_end RET
演算法 備註: 1.由Timer0_Mode1計時計數知,可 計時數範圍由可以計1u~65.536ms 2.再配合電壓0~3V與ADC解析度變化 範圍為0~1023,由ADC改變的Value 帶入Timer0_Mode1之TH0與TL0, 即可計時遙控汽車,發送端PPM變 化量。 3.如圖一所示,表示: (1)當adc_value=0,則計時600uS (2)當adc_value=1023,則計時1600 uS 4.如圖二所示,表示: (a)時間變化量改變地方 (b)時間固定地方 圖一:Adc_value與時間變化量關係圖 圖二:Channel時間定量與變化地方
程式:系統設定 #include <c8051f320.h> /*--------------------------------------定義腳位------------------------*/ sbit PPM = P2^0; /* PPM 訊號輸出腳位 */ /*--------------------------------------變數宣告------------------------*/ unsigned int ch1_get_adc,ch2_get_adc,ch3_get_adc; /*--------------------------------------宣告副程式----------------------*/ int set_ch1_get_adc (void); /* channel adc設定與取adc值 */ int set_ch2_get_adc (void); /* channe2 adc設定與取adc值 */ int set_ch3_get_adc (void); /* channe3 adc設定與取adc值 */ void sys_ini (void); /* c8051f320初始化 */ void timer_delay_high_ch_1(void); /* ch1計時時間變化 */ void timer_delay_high_ch_2(void); /* ch2計時時間變化 */ void timer_delay_high_ch_3(void); /* ch3計時時間變化 */ void timer_delay_low(void); /* 計時固定400us*/ void timer_delay_end(void); /* end_bit計時 */ void ppm_total_signal(void); /* 整體PPM輸出*/
主程式 /*--------------------------------------主程式--------------------------*/ void main ( void ) { sys_ini(); /* c8051f320初始化 */ while(1) set_ch1_get_adc(); /* 設定CH1_ADC與取CH1_ADC轉換值*/ set_ch2_get_adc(); /* 設定CH2_ADC與取CH2_ADC轉換值*/ set_ch3_get_adc(); /* 設定CH3_ADC與取CH3_ADC轉換值*/ ppm_total_signal(); /* PPM整體訊號輸出*/ } void sys_ini (void) OSCICN = 0x83; /*將sysclk由1.5Mhz改成12Mhz*/ RSTSRC = 0x04; /*當timer計時大於100us會有誤差為了防止這樣的問題而做了保護動作*/ PCA0MD=0x00; /*防止timer_counter與soft_counter干擾*/ XBR1 = 0x40; /* 啟動 I/O Port */ REF0CN = 0x08; /* 使用vdd作為參考電壓 */
channel adc設定與取adc值 int set_ch1_get_adc (void) { P1MDIN = 0xFB; /* 設定P1.2為analog_input */ P1SKIP = 0x04; /* 讓P1.2可以輸入analog input*/ /* Init MUX */ AMX0P = 0x02; /* P1.2腳為 analog 訊號進入給adc */ AMX0N = 0x1F; /* single_ended mode*/ /* Init ADC */ ADC0CF = 0x08; /*sampling rate */ ADC0CN = 0x80; /* 整體adc總開關並且使用ad0busy為轉換模式 */ AD0BUSY = 1; /* 當AD0BUSY=1,則開始進行轉換adc_value */ while(AD0BUSY); /* 開始轉換,轉換完畢,旗標變為0 */ ch1_get_adc = ADC0L + (ADC0H<<8); /* 取轉換值方式*/ return ch1_get_adc; /* 將值回傳 */ }
channe2 adc設定與取adc值 int set_ch2_get_adc (void) { P1MDIN = 0xF7; /* 設定P1.3為analog_input */ P1SKIP = 0x08; /* 讓P1.3可以輸入analog input*/ /* Init MUX */ AMX0P = 0x03; /* P1.3腳為 analog 訊號進入給adc */ AMX0N = 0x1F; /* single_ended mode*/ /* Init ADC */ ADC0CF = 0x08; /*sampling rate */ ADC0CN = 0x80; /* 整體adc總開關並且使用ad0busy為轉換模式 */ AD0BUSY = 1; /* 當AD0BUSY=1,則開始進行轉換adc_value */ while(AD0BUSY); /* 開始轉換,轉換完畢,旗標變為0 */ ch2_get_adc = ADC0L + (ADC0H<<8); /* 取轉換值方式*/ return ch2_get_adc; /* 將值回傳 */ }
channe3 adc設定與取adc值 int set_ch3_get_adc (void) { P1MDIN = 0xEF; /* 設定P1.4為analog_input */ P1SKIP = 0x10; /* 讓P1.4可以輸入analog input*/ /* Init MUX */ AMX0P = 0x04; /* P1.2腳為 analog 訊號進入給adc */ AMX0N = 0x1F; /* single_ended mode*/ /* Init ADC */ ADC0CF = 0x08; /*sampling rate */ ADC0CN = 0x80; /* 整體adc總開關並且使用ad0busy為轉換模式 */ AD0BUSY = 1; /* 當AD0BUSY=1,則開始進行轉換adc_value */ while(AD0BUSY); /* 開始轉換,轉換完畢,旗標變為0 */ ch3_get_adc = ADC0L + (ADC0H<<8); /* 取轉換值方式*/ return ch3_get_adc; /* 將值回傳 */ }
void timer_delay_low (void) { TMOD = 0x01; /* 使用 timer0_mode1 模式*/ TH0 = (65536-400)/256; /* 計數400次,時間為400us*/ TL0 = (65536-400)%256; /* 計數400次,時間為400us*/ TR0 = 1; /* 開始計時計數 */ while (TF0 ==0); //while(1) if (TF0 == 1) break; /*等待計時計數完成*/ TF0 = 0; /*清除TF旗標*/ TR0 = 0; /*關閉計數*/ } void timer_delay_high_ch_1 (void) { TMOD = 0x01; /* 使用 timer0_mode1 模式*/ TH0 = (65536-(600+ch1_get_adc))/256; /* 計數600~1600次,時間為600~1600us*/ TL0 = (65536-(600+ch1_get_adc))%256; /* 計數600~1600次,時間為600~1600us*/ TR0 = 1; /* 開始計時計數 */ while (TF0==0); /*等待計時計數完成 */ TF0 = 0; /*清除TF旗標 */ TR0 = 0; /*關閉計數 */ }
void timer_delay_high_ch_2 (void) { TMOD = 0x01; /* 使用 timer0_mode1 模式*/ TH0 = (65536-(600+ch2_get_adc))/256; /* 計數600~1600次,時間為600~1600us*/ TL0 = (65536-(600+ch2_get_adc))%256; /* 計數600~1600次,時間為600~1600us*/ TR0 = 1; /* 開始計時計數 */ while (TF0==0); /*等待計時計數完成 */ TF0 = 0; /*清除TF旗標 */ TR0 = 0; /*關閉計數 */ } void timer_delay_high_ch_3 (void) { TMOD = 0x01; /* 使用 timer0_mode1 模式*/ TH0 = (65536-(600+ch1_get_adc))/256; /* 計數600~1600次,時間為600~1600us*/ TL0 = (65536-(600+ch1_get_adc))%256; /* 計數600~1600次,時間為600~1600us*/ TR0 = 1; /* 開始計時計數 */ while (TF0==0); /*等待計時計數完成 */ TF0 = 0; /*清除TF旗標 */ TR0 = 0; /*關閉計數 */ }
end_bit計時 備註: 65536-(20000-3400-ch3_get_adc-ch2_get_adc-ch1_get_adc) void timer_delay_end(void) { TMOD = 0x01; TH0 = (65536-(20000-3400-ch3_get_adc-ch2_get_adc-ch1_get_adc))/256; TL0 = (65536-(20000-3400-ch3_get_adc-ch2_get_adc-ch1_get_adc))%256; TR0 = 1; while (TF0==0); TF0 = 0; TR0 = 0; } 備註: 65536-(20000-3400-ch3_get_adc-ch2_get_adc-ch1_get_adc) 此計算式子,是由計時計數總週期20ms減去各channel的high與 low的時間。以下圖作為更詳細的說明! 20ms 20ms-各channel之high與low時間
整體訊號輸出 void ppm_total_signal(void) { /* channel 1 */ timer_delay_low(); /*延遲400us*/ PPM = 1; /* 輸出訊號為1 */ timer_delay_high_ch_1(); /*延遲時間600us~1600us*/ /* channel 2 */ PPM = 0; /*輸出訊號為0*/ timer_delay_low(); /*延遲400us*/ PPM = 1; /*輸出訊號為1*/ timer_delay_high_ch_2(); /*延遲時間600us~1600us*/ /* channel 3 */ timer_delay_low(); /*延遲400us*/ timer_delay_high_ch_3(); /*延遲時間600us~1600us*/ /* end bit */ timer_delay_low(); /*延遲400us*/ timer_delay_end(); /*延遲時間由20ms-各channel之延遲時間*/ }
電路實作 JTAG 編碼電路
電路測試 CH 1 Input:0V Time:1ms CH 1 Input:1.61V Time:1.5ms Power Supply:6V CH 1 Input:3.38V Time:2ms
電路測試 CH 1 Time:1.5ms CH 1 Time:1ms CH 1 Time:2ms
電路測試 CH 2 Input:0V Time:1ms CH 2 Input:1.60V Time:1.5ms Power Supply:6V CH 2 Input:3.38V Time:2ms
電路測試 CH 1 Time:1.5ms CH 2 Time:1ms CH 1 Time:2ms
電路測試 CH 3 Input:0V Time:1ms CH 3 Input:1.60V Time:1.5ms Power Supply:6V CH 3 Input:3.38V Time:2ms
電路測試 CH 3 Time:1.5ms CH 3 Time:1ms CH 3 Time:2ms Total time Time:20ms