找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6292|回复: 1
打印 上一主题 下一主题
收起左侧

使用PIC12F683单片机构建简单激光投影仪

[复制链接]
跳转到指定楼层
楼主
翻译+转载作品:使用Microchip PIC12F683单片机构建自己的简单激光投影仪
2010413日,作者:rwb,隶属于Microcontroller


8引脚PIC12F683单片机是Microchip 8位单片机系列中最小的成员之一,但具有强大的外设,例如ADCPWM功能。这使得这个微型微控制器适用于控制直流电动机的速度。为了演示PIC12F683的功能并使本教程更具吸引力,我决定使用PIC12F683微控制器通过廉价的钥匙串激光笔生成简单而又引人入胜的激光表演。
在许多娱乐俱乐部或公园中显示的激光灯基本都使用两种方法。第一个是在观众身上发射激光束,第二个是在屏幕上显示激光绘图图案。在本教程中,我们将使用微型Microchip PIC12F683微控制器构建激光投影仪,在屏幕上显示呼吸描记器图案。
制作呼吸描记器激光投影仪的原理是至少使用两个直流电机,并在其上安装反射镜,然后这些反射镜会将激光束从一个直流电机反射镜偏转到第二个直流电机反射镜,最后偏转到屏幕。通过控制每个直流电动机的旋转速度,我们可以在屏幕上生成引人入胜的激光呼吸描记器图案,如下图所示。
控制直流电动机速度的最佳方法是使用PWM(脉冲波调制)信号来驱动直流电动机,并且由于我们要手动更改直流电动机速度,因此我们需要使用微调端口或电位计来控制每个直流电动机。直流电动机的速度。嗯,这听起来像是微控制器的一项适当工作,但我们可以使用这种8引脚微型PIC12F683微控制器来完成此任务吗?
从数据表中您会注意到,Microchip PIC12F683单片机只有一个PWM输出(CCP1)和四个ADC输入通道(AN0AN1AN2AN3)。由于我们需要两个PWM输出,因此在本教程中,我将向您展示如何基于PIC12F683单片机TIMER0外设生成PWM信号,而不是使用内置在PWM外设中的PIC12F683单片机。以下是激光投影仪项目的完整电子原理图。
好吧,在我们进一步介绍细节之前;让我们列出完成此激光投影仪项目所需的支持外围设备:

  • 热胶枪
  • 钥匙串激光笔或任何可用的激光笔
  • 3xAA4.5伏电池座,用于为激光指示器供电,请使用与激光指示器相同的电压速率。
  • 从废弃的PS2双电击操纵杆中取出两个直流电机
  • 来自田宫赛车的两个玩具车轮胎
  • 镜子的CD / DVD,用厨房剪刀将CD / DVD切成直径约38毫米的两个圆形镜子
  • 一些用于固定直流电动机的玩具塑料积木
  • 面包板
  • 激光投影仪的底座使用硬板或丙烯酸树脂
  • 双面胶带
以下是我用于制作此激光投影仪项目的电子零件和软件开发工具:

  • 电阻器:3303),1K5)和10K1
  • Trimport10K2
  • 电容:100nF2)和10nF1
  • 一个100uH电感器
  • 两个1N4148二极管
  • 两个蓝色和一个红色发光二极管(LED
  • 两个2N2222A晶体管
  • 一个迷你按钮开关
  • 一个Microchip     PIC12F683单片机
  • Microchip MPLAB v8.46 IDE(集成开发环境)
  • Microchip宏汇编器MPASMWIN.exe     v5.35mplink.exe     v4.35
  • PIC10 / 12/16 MCUHI-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(全局中断使能)位,以便当TMR0TIMER0计数器)寄存器溢出(大于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

laserlight_13.jpg (115.97 KB, 下载次数: 133)

laserlight014

laserlight014

laserlight_12.jpg (134.31 KB, 下载次数: 128)

laserlight012

laserlight012

laserlight_11.jpg (93.46 KB, 下载次数: 127)

laserlight011

laserlight011

laserlight_10.jpg (76.29 KB, 下载次数: 123)

laserlight010

laserlight010

laserlight_09.jpg (75.97 KB, 下载次数: 111)

laserlight09

laserlight09

laserlight_04.jpg (51.49 KB, 下载次数: 132)

laserlight04

laserlight04

laserlight_01.jpg (71.47 KB, 下载次数: 127)

laserlight01

laserlight01

laserlight_02.jpg (38.32 KB, 下载次数: 122)

laserlight02

laserlight02

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:631408 发表于 2019-12-8 19:02 | 只看该作者
先收藏为敬,谢谢楼主
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表