翻譯+轉載作品:使用Microchip PIC12F683單片機構建自己的簡單激光投影儀 2010 年4 月13 日,作者:rwb ,隸屬于Microcontroller 。
laserlight_06.jpg (113.69 KB, 下載次數: 99)
下載附件
laserlight06
2019-10-25 23:03 上傳
laserlight_05.jpg (48.17 KB, 下載次數: 76)
下載附件
laserlight05
2019-10-25 23:03 上傳
laserlight_03.jpg (98.83 KB, 下載次數: 92)
下載附件
laserlight03
2019-10-25 23:03 上傳
laserlight_07.jpg (154.75 KB, 下載次數: 79)
下載附件
laserlight07
2019-10-25 23:03 上傳
laserlight_08.jpg (71.75 KB, 下載次數: 82)
下載附件
laserlight08
2019-10-25 23:03 上傳
pic12f683.jpg (57.94 KB, 下載次數: 91)
下載附件
laserlight013
2019-10-25 23:03 上傳
8引腳PIC12F683單片機是Microchip 8位單片機系列中最小的成員之一,但具有強大的外設,例如ADC和PWM功能。這使得這個微型微控制器適用于控制直流電動機的速度。為了演示PIC12F683的功能并使本教程更具吸引力,我決定使用PIC12F683微控制器通過廉價的鑰匙串激光筆生成簡單而又引人入勝的激光表演。 在許多娛樂俱樂部或公園中顯示的激光燈基本都使用兩種方法。第一個是在觀眾身上發射激光束,第二個是在屏幕上顯示激光繪圖圖案。在本教程中,我們將使用微型Microchip PIC12F683微控制器構建激光投影儀,在屏幕上顯示呼吸描記器圖案。 制作呼吸描記器激光投影儀的原理是至少使用兩個直流電機,并在其上安裝反射鏡,然后這些反射鏡會將激光束從一個直流電機反射鏡偏轉到第二個直流電機反射鏡,最后偏轉到屏幕。通過控制每個直流電動機的旋轉速度,我們可以在屏幕上生成引人入勝的激光呼吸描記器圖案,如下圖所示。 控制直流電動機速度的最佳方法是使用PWM(脈沖波調制)信號來驅動直流電動機,并且由于我們要手動更改直流電動機速度,因此我們需要使用微調端口或電位計來控制每個直流電動機。直流電動機的速度。嗯,這聽起來像是微控制器的一項適當工作,但我們可以使用這種8引腳微型PIC12F683微控制器來完成此任務嗎? 從數據表中您會注意到,Microchip PIC12F683單片機只有一個PWM輸出(CCP1)和四個ADC輸入通道(AN0,AN1,AN2和AN3)。由于我們需要兩個PWM輸出,因此在本教程中,我將向您展示如何基于PIC12F683單片機TIMER0外設生成PWM信號,而不是使用內置在PWM外設中的PIC12F683單片機。以下是激光投影儀項目的完整電子原理圖。 好吧,在我們進一步介紹細節之前;讓我們列出完成此激光投影儀項目所需的支持外圍設備:
- 熱膠槍
- 鑰匙串激光筆或任何可用的激光筆
- 3xAA,4.5伏電池座,用于為激光指示器供電,請使用與激光指示器相同的電壓速率。
- 從廢棄的PS2雙電擊操縱桿中取出兩個直流電機
- 來自田宮賽車的兩個玩具車輪胎
- 鏡子的CD / DVD,用廚房剪刀將CD / DVD切成直徑約38毫米的兩個圓形鏡子
- 一些用于固定直流電動機的玩具塑料積木
- 面包板
- 激光投影儀的底座使用硬板或丙烯酸樹脂
- 雙面膠帶
以下是我用于制作此激光投影儀項目的電子零件和軟件開發工具:
- 電阻器:330(3),1K(5)和10K(1)
- Trimport:10K(2)
- 電容:100nF(2)和10nF(1)
- 一個100uH電感器
- 兩個1N4148二極管
- 兩個藍色和一個紅色發光二極管(LED)
- 兩個2N2222A晶體管
- 一個迷你按鈕開關
- 一個Microchip PIC12F683單片機
- Microchip MPLAB v8.46 IDE(集成開發環境)
- Microchip宏匯編器MPASMWIN.exe v5.35,mplink.exe v4.35
- PIC10 / 12/16 MCU的HI-TECH C編譯器(精簡模式)V9.70
- Microchip PICKit3編程器(固件套件版本:01.25.20)
該項目的目標是為持續教訓我以前發布博客介紹PIC匯編語言部分-1和介紹PIC匯編語言部分-2,所以我用的第2部分呈現相同的PIC12F683板,你可以降負荷兩者的以EagleCAD格式設計的電子原理圖和PCB布局。該激光投影儀項目的另一個有趣特征是:除了PIC匯編代碼外,我還為C語言愛好者提供了該項目的C語言版本,并使用HI-TECH C編譯器進行了編譯(最近HI-TECH軟件已被Microchip收購)。此C語言版本可用于學習以及嵌入式系統編程語言比較。 在該項目中,我還使用了一個新的Microchip PICKit3編程器,但是您當然可以使用Microchip PICKit2編程器將十六進制代碼下載到PIC12F683單片機閃存中。 以下是PIC匯編語言中的Laser Projector代碼: ;******************************************************************************
; File Name : laserlight.asm
; Version : 1.0
; Description : Laser Light ShowProject
; Author : RWB
; Target : Microchip PIC12F683Microcontroller
; Compiler : Microchip Assembler(MPASMWIN.exe v5.35, mplink.exe v4.35)
; IDE : Microchip MPLAB IDEv8.46
; Programmer : PICKit3 (FirmwareSuite Version: 01.25.20)
; Last Updated : 01 April 2010
; *****************************************************************************
#include <p12F683.inc>
__config (_INTRC_OSC_NOCLKOUT &_WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF &_FCMEN_OFF)
#define MAX_TMR0 0xFB
#define MAX_COUNT .200
#define MAX_DEBOUNCE 0x0A
#define MAX_TBLINDEX 0x0A
; Define variables used
cblock 0x20
Delay:2 ; Define two registersfor the Delay and Delay + 1
mode ; Operation Mode
pwm_count ; Hold the Main PWM Counter
pwm_m1 ; Hold the PWMwidth for Motor 1
pwm_m2 ; Hold the PWM width forMotor 2
keycount ; Debounce Count
tableindex ; Table Index for Auto PWM
endc
; Define variable use for storing STATUSand WREG register
cblock 0x70 ; Useunbanked RAM, available both in Bank0 and Bank1
saved_w
saved_status
endc
; Start the Light show Assembler Code here
org 0x00 ; Wealways start at flash address 0
goto Main ; Jump toMain
org 0x04 ; 0x04:Start PIC Interrupt Address
PIC_ISR: ; Start the PIC InterruptService Routine
movwf saved_w ; Save Working Register
movf STATUS,w ; Save Status Register
movwf saved_status
; Check the TIMER0 Interrupt here
btfss INTCON,T0IF
goto ExitISR ; If (T0IF != 1) then Exit ISR
bcf STATUS,RP0 ; Select Registers at Bank 0
incf pwm_count ; pwm_count++
movlw MAX_COUNT
subwf pwm_count,w ; if (pwm_count < MAX_COUNT) thenCheckPWM
btfss STATUS,C ; else clear GP1 and GP2
goto CheckPWM
bcf GPIO,GP1 ; GPIO1=0
bcf GPIO,GP2 ; GPIO2=0
goto ExitPWM
CheckPWM:
movf pwm_m1,w
subwf pwm_count,w
btfsc STATUS,Z ; if (pwm_count == pwm_m1) then Set GP1
bsf GPIO,GP1 ; Set GP1 Bit
CheckM2:
movf pwm_m2,w
subwf pwm_count,w
btfsc STATUS,Z ; if (pwm_count == pwm_m2) then Set GP2
bsf GPIO,GP2 ; Set GP2 bit
ExitPWM:
bcf INTCON,T0IF ; clear the TIMER0 interrupt flag
movlw MAX_TMR0
movwf TMR0 ; TMR0 = MAX_TMR0
ExitISR:
movf saved_status,w
movwf STATUS ; Restore STATUS Register
swapf saved_w,f
swapf saved_w,w ; Restore W Register
retfie ; Returnfrom Interrupt
Main:
bsf STATUS,RP0 ; Select Registers at Bank 1
movlw 0x70
movwf OSCCON ; Set the internal clock speed to 8MHz
movlw 0x39 ; GP1 and GP2 Output, GP0,GP3,GP4and GP5 as Input
movwf TRISIO ; TRISIO = 0x39
bcf STATUS,RP0 ; Select Registers at Bank 0
movlw 0x07
movwf CMCON0 ; Turn off Comparator (GP0, GP1, GP2)
clrf GPIO
; Now we Set the ADC Peripheral
bsf STATUS,RP0 ; Select Registers at Bank 1
movlw 0x79 ; Set AN0 (GP0) and AN3 (GP4) asAnalog Input
movwf ANSEL ; Using the Internal Clock(FRC)
; Now we set the TIMER0 Peripheral
; TIMER0 Period = 1/FSOC x 4 x Prescale xTMR0
movlw 0x00 ; Use TIMER0 Prescaler 1:2, InternalClock
movwf OPTION_REG ; OPTION_REG = 0x00
bcf STATUS,RP0 ; Select Registers at Bank 0
movlw MAX_TMR0
movwf TMR0 ; TMR0=MAX_TMR0
; Initial the variables used
clrf mode ; Default mode = 0, Light Show Off
clrf pwm_count ; pwm_count = 0
clrf pwm_m1 ; pwm_m1 = 0
clrf pwm_m2 ; pwm_m2 = 0
clrf keycount ; keycount = 0
clrf tableindex ; tableindex = 0
; Activate the Interrupt
bsf INTCON,GIE ; Enable Global Interrupt
MainLoop:
btfsc GPIO,GP5 ; Now we check the Button
goto CheckMode ; if (GP5 != 0) goto CheckMode
movlw 0x01
addwf keycount ; keycount=keycount + 1
movf keycount,w
sublw MAX_DEBOUNCE
btfss STATUS,C ; if (keycount > MAX_DEBOUNCE) gotoKeyPressed
goto KeyPressed
goto CheckMode ; else CheckMode
KeyPressed:
clrf keycount ; keycount=0
incf mode ; mode++
movlw 0x03
subwf mode,w ; W = mode - 0x03
btfsc STATUS,C ; if (mode >= 0x03)
clrf mode ; mode=0;
movlw 0x01 ; else check the mode
subwf mode,w
btfss STATUS,C ; if (mode >= 0x01) goto TurnOn
goto TurnOff ; else goto TurnOff
goto TurnOn
TurnOff:
bcf INTCON,T0IE ; Disable TIMER0 Interrupt
clrf pwm_count ; pwm_count = 0
clrf pwm_m1 ; pwm_m1 = 0
clrf pwm_m2 ; pwm_m2 = 0
bcf GPIO,GP1
bcf GPIO,GP2
movlw .250
call DelayMs ; DelayMs(250)
movlw .250
call DelayMs ; DelayMs(250)
goto CheckMode
TurnOn:
bsf INTCON,T0IE ; Enable TIMER0 Interrupt
CheckMode:
movlw 0x01
subwf mode,w
btfss STATUS,Z ; if (mode == 1) goto ShowMode1
goto CheckMode2
goto ShowMode1
CheckMode2:
movlw 0x02
subwf mode,w
btfss STATUS,Z ; if (mode == 2) goto ShowMode2
goto KeepLoop
goto ShowMode2
ShowMode1: ; Used ADC for PWM
movlw B'00000001' ; Left Justify and turn on the ADCperipheral, channel 0 (AN0)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 0(AN0)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Loop
call Delay1ms
movlw B'00000001' ; Left Justify and turn on the ADCperipheral, channel 0 (AN0)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 0(AN0)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Loop
movf ADRESH,w ; Conversion Done, Read ADRESH
movwf pwm_m1 ; pwm_m1 = ADRESH
call Delay1ms
movlw B'00001101' ; Left Justify and turn on the ADCperipheral, channel 3 (AN3)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 3(AN3)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Test
call Delay1ms
movlw B'00001101' ; Left Justify and turn on the ADCperipheral, channel 3 (AN3)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 3(AN3)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Test
movf ADRESH,w ; Conversion Done, Read ADRESH
movwf pwm_m2 ; pwm_m2 = ADRESH
call Delay1ms
goto KeepLoop
ShowMode2: ; Used Predefined Value forPWM
movf tableindex,w
call tablepwm1 ; Call tablepwm1
movwf pwm_m1 ; Assigned it to pwm_m1
movlw .30
call DelayMs ; DelayMs(30)
movf tableindex,w
call tablepwm2 ; Call tablepwm2
movwf pwm_m2 ; Assigned it to pwm_m2
movlw .30
call DelayMs ; DelayMs(30)
incf tableindex ; tableindex++
movlw MAX_TBLINDEX
subwf tableindex,w
btfss STATUS,C ; if (tableindex >= 0x0A) thentableindex = 0
goto KeepLoop
clrf tableindex ; tableindex = 0
KeepLoop:
goto MainLoop ; Goto MainLoop
; Predefined value table for Automatic PWM
tablepwm1:
addwf PCL,f
retlw 0x10
retlw 0x5A
retlw 0x9A
retlw 0x20
retlw 0x40
retlw 0x8A
retlw 0x82
retlw 0x30
retlw 0x58
retlw 0xAA
tablepwm2:
addwf PCL,f
retlw 0x70
retlw 0x8A
retlw 0x2A
retlw 0x30
retlw 0x1C
retlw 0x2A
retlw 0x4B
retlw 0xA0
retlw 0x18
retlw 0x2A
;----------------- DelayMs: MillisecondDelay Subroutine ----------------------
; Paramater: WREG = delay amount in milisecond,max: 255 millisecond
DelayMs:
movwf Delay + 1
DelayLoop:
call Delay1ms
decfsz Delay + 1,f ; Decrease Delay + 1, If zero skip thenext instruction
goto DelayLoop ; Not zero goto DelayLoop
return ; return to the caller
;----------------- Delay1ms: 1 ms DelaySubroutine ---------------------------
Delay1ms: ; Total Delay: 1998 x 0.5us~ 1 ms
movlw 0x99
movwf Delay
DelayLoop1:
decfsz Delay,f ; Decrease Delay, If zero skip thenext instruction
goto DelayLoop1
DelayLoop2:
decfsz Delay,f ; Decrease Delay, If zero skip thenext instruction
goto DelayLoop2 ; Not zero goto DelayLoop2
DelayLoop3:
decfsz Delay,f ; Decrease Delay, If zero skip thenext instruction
goto DelayLoop3 ; Not zero goto DelayLoop2
return ; Returnto the caller
end
; EOF: laserlight.asm
The following is the Laser ProjectorProject code in C Language version:
//***************************************************************************
// File Name : laserlight.c
// Version : 1.0
// Description : Laser Light ShowProject
// Author : RWB
// Target : Microchip PIC12F683Microcontroller
// Compiler : HI-TECH CPIC10/12/16 MCUs (Lite Mode) V9.70
// IDE : Microchip MPLAB IDEv8.46
// Programmer : PICKit3 (FirmwareSuite Version: 01.25.20)
// Last Updated : 03 April 2010
// ***************************************************************************
#include <pic.h>
/* PIC Configuration Bit:
** INTIO - Using Internal RC NoClock
** WDTDIS - Wacthdog Timer Disable
** PWRTEN - Power Up Timer Enable
** MCLRDIS - Master Clear Disable
** UNPROTECT - Code Un-Protect
** UNPROTECT - Data EEPROM Read Un-Protect
** BORDIS - Borwn Out DetectDisable
** IESODIS - Internal ExternalSwitch Over Mode Disable
** FCMDIS - Monitor Clock FailSafe Disable
*/
__CONFIG(INTIO & WDTDIS & PWRTEN& MCLRDIS & UNPROTECT \
& UNPROTECT & BORDIS & IESODIS & FCMDIS);
// Using Internal Clock of 8 MHz
#define FOSC 8000000L
#define MAX_COUNT 200
#define MAX_TMR0 0xFB
#define MAX_DEBOUNCE 0x0A
#define MAX_TBLINDEX 0x0A
unsigned char pwm_count=0;
unsigned char pwm_m1=0;
unsigned char pwm_m2=0;
unsigned chartablepwm1[10]={0x10,0x5A,0x9A,0x20,0x40,0x8A,0x82,0x30,0x58,0xAA};
unsigned chartablepwm2[10]={0x70,0x8A,0x2A,0x30,0x1C,0x2A,0x4B,0xA0,0x18,0x2A};
unsigned char tableindex=0;
/* The Delay Function */
#define delay_us(x){ unsigned char us; \
us = (x)/(12000000/FOSC)|1; \
while(--us != 0) continue; }
void delay_ms(unsigned int ms)
{
unsigned char i;
do{
i= 4;
do {
delay_us(164);
}while(--i);
}while(--ms);
}
static void interrupt isr(void)
{
if(T0IF) { // TIMER0 Interrupt Flag
pwm_count++; // PWM CountIncrement
if (pwm_count >= MAX_COUNT) {
pwm_count=0;
GPIO1=0; // Turn off GP1
GPIO2=0; // Turn off GP2
}
if (pwm_count == pwm_m1) {
GPIO1=1; // Turn On GP1
}
if (pwm_count == pwm_m2) {
GPIO2=1; // Turn On GP2
}
TMR0 = MAX_TMR0; // InitialValue for TIMER0 Interrupt
T0IF = 0; // Clear TIMER0 interrupt flag
}
}
void main(void)
{
unsigned char mode,keycount;
OSCCON=0x70; // Select 8MHz internal clock
/*Initial Port Used */
TRISIO = 0x39; // GP1 andGP2 Output, GP0,GP3,GP4 and GP5 as Input
CMCON0 = 0x07; // Turn offComparator (GP0, GP1, GP2)
GPIO = 0x00; // Turn Offall IO
/*Init ADC Peripheral */
ANSEL = 0x79; // Set AN0(GP0) and AN3 (GP4) as Analog Input, Internal Clock
/*Init TIMER0: TIMER0 Period = 1/FSOC x 4 x Prescale x TMR0*/
OPTION = 0b00000000; // 1:2 Prescale
TMR0=MAX_TMR0 ;
/*Init Variable Used */
pwm_count=0;
pwm_m1=0;
pwm_m2=0;
mode=0;
keycount=0;
tableindex=0;
GIE=1; // Enable Global Interrupt
for(;;) {
// Display the LED
if (GPIO5 == 0) {
keycount++;
if (keycount > MAX_DEBOUNCE) {
keycount=0;
mode = mode + 1;
if (mode > 2) mode = 0;
if (mode >= 0x01) {
T0IE = 1; // Enable TIMER0 Interrupt on Overflow
} else {
T0IE = 0; // Disable TIMER0 Intterupt on Overflow
pwm_count=0;
pwm_m1=0;
pwm_m2=0;
GPIO1=0; // Turn offGP1
GPIO2=0; // Turn offGP2
delay_ms(500);
}
}
}
if (mode == 1) {
/* Read the ADC here */
ADCON0=0b00000001; // selectleft justify result. ADC port channel AN0
GODONE=1; // initiate conversion on thechannel 0
while(GODONE) continue; // Waitfor ldr_left conversion done
pwm_m1=ADRESH; // Read 8bits MSB, Ignore 2 bits LSB in ADRESL
delay_ms(1);
/* Read the ADC here */
ADCON0=0b00001101; // selectleft justify result. ADC port channel AN3
GODONE=1; // initiate conversion on the channel4
while(GODONE) continue; // Waitfor ldr_left conversion done
pwm_m2=ADRESH; // Read 8bits MSB, Ignore 2 bits LSB in ADRESL
delay_ms(1);
}
if (mode == 2) {
pwm_m1=tablepwm1[tableindex];
delay_ms(10);
pwm_m2=tablepwm2[tableindex];
delay_ms(10);
tableindex++;
if (tableindex >= MAX_TBLINDEX)
tableindex = 0;
}
}
}
/* EOF: laserlight.c */
產生PWM輸出的簡單方法是利用PIC12F683單片機的PWM內置功能,但是由于PIC12F683僅支持一個PWM輸出,因此我們需要找到一種方法來模仿固件中的這種外設行為。大多數微控制器PWM外設都使用TIMER外設來產生脈沖。在PIC12F683中,PWM發生器使用TIMER2生成PWM輸出(CCP1),如下圖所示:
當TIMER2開始計數時,TMR2寄存器(TIMER2計數器寄存器)的值會不斷與PR2和CCPR1L寄存器進行比較。當TMR2等于CCPR1L值時,CCP1輸出將被復位;而當TMR2等于PR2值時,它將設置CCP1輸出。因此,通過如上圖所示更改CCPR1L寄存器的值,我們可以更改PWM占空比(脈沖寬度)。您可以在我以前的博客上閱讀有關使用PIC單片機PWM外設的原理的更多信息。H橋Microchip PIC單片機PWM電機控制器。
使用相同的原理,我在Microchcip PIC12F683微控制器TIMER0外設的固件中制作了PWM發生器,如下圖所示:
file:///C:/Users/User1/AppData/Local/Temp/msohtmlclip1/01/clip_image022.jpg
TIMER0基本PWM發生器的基本原理是使用TIMER0溢出中斷來增加我們的PWM計數器變量pwm_count,如果該變量達到MAX_COUNT(200),則我們將復位GP1和GP2輸出引腳。如果不同,則將其與PWM占空比控制變量pwm_m1和pwm_m2進行比較,如果相等,則只需設置GP1或GP2輸出引腳即可。現在,使用該原理,我們可以輕松地基于PIC12F683單片機TIMER0外設生成高效的PWM信號。
file:///C:/Users/User1/AppData/Local/Temp/msohtmlclip1/01/clip_image024.jpg
現在看一下實現PIC12F683單片機TIMER0外設基本PWM發生器的PIC匯編代碼:
; Checkthe TIMER0 Interrupt here
btfss INTCON,T0IF
goto ExitISR ; If (T0IF != 1)then Exit ISR
bcf STATUS,RP0 ; Select Registersat Bank 0
incf pwm_count ; pwm_count++
movlw MAX_COUNT
subwf pwm_count,w ; if (pwm_count< MAX_COUNT) then CheckPWM
btfss STATUS,C ; else clear GP1and GP2
goto CheckPWM
bcf GPIO,GP1 ; GPIO1=0
bcf GPIO,GP2 ; GPIO2=0
goto ExitPWM
該例程首先通過檢查INTCON寄存器上的T0IF位來檢查該中斷是否由TIMER0外設產生,如果此中斷來自TIMER0,則T0IF位將被置1(邏輯“ 1 ”),然后我們繼續增加pwm_count變量,并如果達到MAX_COUNT,我們將重置GP1和GP2輸出引腳。接下來,如果pwm_count尚未達到MAX_COUNT,則將其值與pwm_m1和pwm_m2變量進行比較,如以下代碼所示:
heckPWM:
movf pwm_m1,w
subwf pwm_count,w
btfsc STATUS,Z ; if (pwm_count == pwm_m1) then Set GP1
bsf GPIO,GP1 ; Set GP1 Bit
CheckM2:
movf pwm_m2,w
subwf pwm_count,w
btfsc STATUS,Z ; if (pwm_count == pwm_m2) then Set GP2
bsf GPIO,GP2 ; Set GP2 bit
當pwm_count等于pwm_m1或pwm_m2時,我們分別將GP1和GP2輸出引腳設置為“邏輯1 ” 。
要計算PWM周期,首先我們必須使用以下公式計算TIMER0周期:
TIMER0 Period = 1/Fosc x 4 x Prescale x (TMR0 + 1)
在本教程中,我們使用1:2預分頻比,TMR0 =251(0xFB)和8 MHz內部振蕩器,因此TIMER0將在以下時間中斷:
TIMER0 Period = 1/8000000 x 4 x 2 x (256 – 251)= 0.000005 second
每次TIMER0中斷(TMR0溢出)時,我們都會增加pwm_count變量,直到達到MAX_COUNT(200),然后將GP1和GP2的每個輸出引腳復位。因此,大約PWM周期可以計算如下:
PWM Period = MAX_COUNT x 0.000005 seconds = 200 x0.000005 = 0.001 second
PWM Frequency = 1 / T = 1/0.001 = 1000 Hz = 1KHz
改變直流電動機的速度
該激光投影儀項目具有兩種模式,用于控制兩個直流電動機的速度,第一個模式是使用微調端口(VR1和VR2),在該模式下,我們使用PIC12F683單片機讀取由這些微調端口(分壓器電路)產生的電壓電平。數字轉換器(ADC)外圍設備,然后應用這些ADC值來更改每個直流電動機速度。第二種方法是使用存儲在查找表中的固定值,然后分配這些值以更改每個直流電動機速度。
Microchip PIC12F683單片機具有四個可用的ADC通道。在此項目中,我們使用通道0(AN0)和通道4(AN3)。通過選擇ADCON0寄存器(ADC控制寄存器)上的requireADC通道并選擇左對齊結果(ADFM = 0),我們可以讀取ADRESH寄存器中的VR1和VR2調整端口產生的電壓值,并將這些值分配給pwm_m1和pwm_m2變量,如以下PIC匯編代碼所示:
ShowMode1: ; Used ADC for PWM
movlw B'00000001' ; Left Justify and turn on the ADCperipheral, channel 0 (AN0)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 0(AN0)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Loop
call Delay1ms
movlw B'00000001' ; Left Justify and turn on the ADCperipheral, channel 0 (AN0)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 0 (AN0)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Loop
movf ADRESH,w ; Conversion Done, Read ADRESH
movwf pwm_m1 ; pwm_m1 = ADRESH
call Delay1ms
movlw B'00001101' ; Left Justify and turn on the ADCperipheral, channel 3 (AN3)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 3(AN3)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Test
call Delay1ms
movlw B'00001101' ; Left Justify and turn on the ADCperipheral, channel 3 (AN3)
movwf ADCON0 ; Vreff=Vdd
bsf ADCON0,GO ; Start the ADC Conversion on channel 3(AN3)
btfss ADCON0,GO ; while(GO == 1)
goto $-1 ; Keep Test
movf ADRESH,w ; Conversion Done, Read ADRESH
movwf pwm_m2 ; pwm_m2 = ADRESH
call Delay1ms
goto KeepLoop
有關使用PICADC外設的更多信息,請閱讀我以前發布的博客PIC模數轉換器C編程。
接下來是自動模式,該模式將為PIC程序閃存上的每個PWM值分配預定值。通過使用PIC匯編器“ retlw k ”(在W寄存器上返回文字值k )指令,我們可以輕松地檢索這些值。以下PIC匯編代碼顯示了我們如何執行此操作:
ShowMode2: ; Used Predefined Value forPWM
movf tableindex,w
call tablepwm1 ; Call tablepwm1
movwf pwm_m1 ; Assigned it to pwm_m1
movlw .30
call DelayMs ; DelayMs(30)
movf tableindex,w
call tablepwm2 ; Call tablepwm2
movwf pwm_m2 ; Assigned it to pwm_m2
movlw .30
call DelayMs ; DelayMs(30)
incf tableindex ; tableindex++
movlw MAX_TBLINDEX
subwf tableindex,w
btfss STATUS,C ; if (tableindex >= 0x0A) thentableindex = 0
goto KeepLoop
clrf tableindex ; tableindex = 0
...
...
; Predefined value table for Automatic PWM
tablepwm1:
addwf PCL,f
retlw 0x10
retlw 0x5A
retlw 0x9A
retlw 0x20
retlw 0x40
retlw 0x8A
retlw 0x82
retlw 0x30
retlw 0x58
retlw 0xAA
tablepwm2:
addwf PCL,f
retlw 0x70
retlw 0x8A
retlw 0x2A
retlw 0x30
retlw 0x1C
retlw 0x2A
retlw 0x4B
retlw 0xA0
retlw 0x18
retlw 0x2A
此處的原理是修改PCL(程序計數器加載)寄存器。所述PCL與所述微控制器PIC12F683程序計數器至少顯著字節和在一起PCLATH寄存器形成PIC12F683微控制器13位寬程序計數器。“ goto ”命令行為可以通過操縱PCL內容來實現,例如:
movlw 0x01
call tablepwm2
nop
tablepwm2:
addwf PCL,f
retlw 0x70
retlw 0x8A
retlw 0x2A
當PIC單片機運行“ 調用tablepwm ”指令時,它將立即將程序計數器(PCL)更改為“ tablepwm2 ”標簽所指的程序地址,即“ addwfPCL,f ”指令。當PIC微控制器執行該指令時,它將W寄存器(0x01)的內容添加到PCL寄存器中,并且PIC微控制器立即跳轉到“ PCL+ 0x01 ”程序地址。接著,它會執行“ RETLW0x8A ”指令,它簡單地返回到與主叫方0x8A的值Wˉˉ寄存器。
因此,通過增加tableindex(查找表指針)變量,我們可以輕松地在pwm_m1和pwm_m2變量中的每個變量上分配預定值。
PIC組裝的基本條件
特別是對于初學者來說,理解PIC匯編指令中令人困惑的部分之一是匯編代碼中的“ 如何實現if條件 ”。在此激光投影儀項目的PIC匯編代碼中,您注意到它使用了很多“ 如果條件 ”以使其起作用。PIC匯編程序的基本“ if condition ”可以使用以下第一個模板代碼來構建:
movf var_1,w ; w = var_1
subwf var_2,w ; w = var_2 - var_1
btfss STATUS,C ; if (var_2 >= var_1)goto label_2
goto label_1 ; else goto label_1
goto label_2
第一個代碼(movfvar_1,w)指示PIC微控制器將var_1的內容放入W(工作)寄存器。第二個代碼(subwfvar_2,w)指示PIC微控制器用W寄存器的內容減去var_2變量的內容,并將結果放入W寄存器中,或者我們可以記為:
W = var_2-var_1
第三碼(BTFSS - b它噸 EST ˚F寄存器,小號硤如果小號等)指示PIC微控制器,如果檢查Ç在(進位)位STATUS寄存器被設置(邏輯“ 1 “),如果將它設置然后跳到一條指令(PIC單片機實際上會自動將下一條指令gotolabel_1替換為nop指令),并執行“ goto label_2 ”代碼。如果C位清零(邏輯“ 0 ”),它將執行“ goto label_1 ”代碼。
如果var_2變量的值大于或等于var_1變量,則STATUS寄存器中的C(進位)位將始終置1(邏輯“ 1 ”)。接下來,以下代碼顯示了第二個“ if condition ”模板:
movf var_1,w ; w = var_1
subwf var_2,w ; w = var_2 - var_1
btfss STATUS,Z ; if (var_2 == var_1)goto label_2
goto label_1 ; else goto label_1
goto label_2
這一次,我們將研究Ž(零)在比特STATUS寄存器,該Ž(零)位將總是設置(邏輯“ 1 “)時,VAR_2值等于VAR_1值。最后,以下代碼顯示了第三個“ ifcondition ”模板:
movlw 0x08 ; w = 0x08
subwf var_1,w ; w = var_1 - 0x08
btfss STATUS,C ; if (var_1 >= 0x08)goto label_2
goto label_1 ; else goto label_1
goto label_2
第三個模板只是第一個模板的變體,這次我們將字面值0x08加載到W寄存器中,并將其與var_1變量進行比較。通過僅使用這三個“ ifcondition ”模板,您可以輕松地在代碼中實現多種類型的“ ifcondition ”。關鍵要理解和執行PIC匯編“ 如果條件 ”在你的代碼指令成功地,始終如一地使用這些模板,一旦你了解比你能修改或用結合起來BTFSC(b它牛逼 EST ˚F寄存器,小號基普如果Çlear)(bit test f register, skipif clear)指令或影響STATUS寄存器中C和Z位的任何PIC匯編程序指令,使您的匯編代碼更高效。
為了使PIC匯編代碼更易于理解,在此激光投影儀教程中,我在代碼中添加了很多注釋,您還可以將其與為該項目提供的等效C語言代碼進行比較。C語言代碼使用與PIC匯編代碼中相同的變量名,以便于比較。
PIC匯編程序代碼內部
激光投影儀項目使用PIC12F683單片機TIMER0和ADC外設來控制每個直流電動機的速度。以下說明用于初始化PIC12F683外設
Main:
bsf STATUS,RP0 ; Select Registers at Bank 1
movlw 0x70
movwf OSCCON ; Set the internal clock speed to 8MHz
movlw 0x39 ; GP1 and GP2 Output, GP0,GP3,GP4and GP5 as Input
movwf TRISIO ; TRISIO = 0x39
bcf STATUS,RP0 ; Select Registers at Bank 0
movlw 0x07
movwf CMCON0 ; Turn off Comparator (GP0, GP1, GP2)
clrf GPIO
; Now we Set the ADC Peripheral
bsf STATUS,RP0 ; Select Registers at Bank 1
movlw 0x79 ; Set AN0 (GP0) and AN3 (GP4) as Analog Input
movwf ANSEL ; Using the Internal Clock(FRC)
; Now we set the TIMER0 Peripheral
; TIMER0 Period = 1/FSOC x 4 x Prescale x TMR0
movlw 0x00 ; Use TIMER0 Prescaler 1:2,Internal Clock
movwf OPTION_REG ; OPTION_REG = 0x00
bcf STATUS,RP0 ; Select Registers at Bank 0
movlw MAX_TMR0
movwf TMR0 ; TMR0=MAX_TMR0
; Initial the variables used
clrf mode ; Default mode = 0, Light Show Off
clrf pwm_count ; pwm_count = 0
clrf pwm_m1 ; pwm_m1 = 0
clrf pwm_m2 ; pwm_m2 = 0
clrf keycount ; keycount = 0
clrf tableindex ; tableindex = 0
; Activate the Interrupt
bsf INTCON,GIE ; Enable Global Interrupt
第一條指令是將OSCCON(振蕩器控制)寄存器設置為使用8MHz內部時鐘,然后我們將TRISIO(三態I / O控制器緩沖器)寄存器設置為輸入和輸出端口,并關閉CMCON0上的比較器外設(比較器控制)寄存器。復位GPIO(通用I / O)寄存器后,我們繼續設置ADC外設的ANSEL(模擬選擇)寄存器和TIMER0外設的OPTION_REG,并通過清除它們來初始化程序中使用的所有變量。 在進入無限循環之前,我們立即激活INTCON(中斷控制)寄存器中的GIE(全局中斷使能)位,以便當TMR0(TIMER0計數器)寄存器溢出(大于255)時發生TIMER0中斷。 在無限循環內(MainLoop標簽),我們不斷檢查模式變量。該模式值被用于確定所述程序的行為。當模式值為0時,程序將簡單地連續循環;當模式值為1時,程序將使用ADC值來控制每個直流電動機速度;而當模式值為2時,程序將使用外觀-上表來控制PWM信號(自動模式)。 PIC匯編代碼和C代碼比較 由于在此項目中,我將PIC匯編語言和C語言用于固件;因此,比較兩個十六進制程序的大小會很有趣。以下是由MicrochipPIC匯編器和HI-TECH C PRO(精簡模式)編譯器生成的HEX代碼。 HI-TECH C PRO編譯器生成的HEX代碼在“ 精簡模式 ” 下為356字(623字節),但是,如果在“ PRO模式 ” 下編譯C代碼,則其大小將減小40%或約為214字( 374.5字節),另一方面,Microchip宏匯編器為HEX代碼生成180個字(315字節)。現在您可以看到,如果我們使用專業的C語言編譯器,它將生成非常小的HEX代碼,幾乎與匯編語言生成的HEX代碼相等。 下載十六進制代碼 在編譯并模擬了代碼之后,是時候使用MicrochipPICKit3編程器下載代碼了,將PICKit3ICSP(微電路在線串行編程)端口連接到PIC12F683單片機引腳,然后從Programmer菜單中選擇Programmer-> PicKit3,然后選擇Programmer ->程序菜單,將您的十六進制代碼下載到PIC12F683單片機閃存中。
原文鏈接:
游客,本帖隱藏的內容需要積分高于 1 才可瀏覽,您當前積分為 0
|