Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller




The 8 pins PIC12F683 microcontroller is one of the smallest members of the Microchip 8-bit microcontroller families but equipped with powerful peripherals such as ADC and PWM capabilities. This make this tiny microcontroller is suitable for controlling the DC motor speed. In order to demonstrate the PIC12F683 capabilities and to make this tutorial more attractive, I decided to use the PIC12F683 microcontroller to generate simple and yet fascinating laser light show from a cheap keychain laser pointer.

Building your own Simple Laser Projector

The basic of laser light shown in many entertainments club or park mostly use two method; the first one is to beam the laser shower on the spectators and the second one is to display the laser drawing pattern on the screen. On this tutorial we are going to build the laser projector that displays the spirograph pattern on the screen using the tiny Microchip PIC12F683 microcontroller.

The principle of making the spirograph laser projector is to use at least two DC motors with the attached mirror on it, these mirrors then will deflect the laser beam from one DC motor mirror to the second DC motor mirror and then finally to the screen. By controlling each of the DC motors spinning speed we could generate a fascinating laser spirograph pattern on the screen as shown on this following picture.

The best way to control the DC motor speed is to use the PWM (pulse wave modulation) signal to drive the DC Motor and because we want to change the DC motor speed manually, therefore we need to use the trimport or potentiometer to control each of the DC motors speed. Hmm, this sound like an appropriate job for the microcontroller but could we use this tiny 8 pins PIC12F683 microcontroller to handle this task?

From the datasheet you will notice that the Microchip PIC12F683 microcontroller only has one PWM output (CCP1) and four ADC input channel (AN0, AN1, AN2 and AN3). Because we need two PWM output, therefore instead of using the PIC12F683 microcontroller build in PWM peripheral, in this tutorial I will show you how to generate the PWM signal base on the PIC12F683 microcontroller TIMER0 peripheral. The following is the complete electronic schematic for the laser projector project.

Ok before we go further with the detail; let’s list down the supporting peripherals needed to complete this laser projector project:

  • Hot glue gun
  • Keychain laser pointer or any available laser pointer
  • 3xAA, 4.5 volt battery holder for powering the laser pointer, please use the same voltage rate used by your laser pointer.
  • Two DC motor taken from the discarded PS2 Dual shock joystick
  • Two toy’s car tire taken from tamiya racing car
  • CD/DVD for the mirror, use a kitchen scissor to cut the CD/DVD into the two circle shape mirror with approximately 38 mm in diameter
  • Some toys plastic bricks for holding the DC motor
  • Breadboard
  • Hardboard or acrylic is used for the base of our laser projector
  • Double Tape

The following are the electronic parts and the software development tools that I used to make this laser projector project:

  • Resistor: 330 (3), 1K (5) and 10K (1)
  • Trimport: 10K (2)
  • Capacitor: 100nF (2) and 10nF (1)
  • One 100uH Inductor
  • Two 1N4148 Diodes
  • Two Blue and one Red Light Emitting Diode (LED)
  • Two 2N2222A transistors
  • One Mini Push Button Switch
  • One Microchip PIC12F683 Microcontroller
  • Microchip MPLAB v8.46 IDE (Integrated Development Environment)
  • Microchip Macro Assembler MPASMWIN.exe v5.35, mplink.exe v4.35
  • HI-TECH C Compiler for PIC10/12/16 MCUs (Lite Mode) V9.70
  • Microchip PICKit3 Programmer (Firmware Suite Version: 01.25.20)
READ  PBUS - an RS485-like multi-drop bus with half duplex serial protocol

This project is aim as the continuing lessons to my previous posted blog Introduction to PIC Assembly Language Part-1 and Introduction to PIC Assembly Language Part-2, therefore I used the same PIC12F683 board presented in the part 2 which you could down load both of the electronic schematic and the PCB layout designed in Eagle CAD format. The other interesting feature of this laser projector project is; besides the PIC assembly code I also provide the C language version of this project for the C language lover and is compiled with the HI-TECH C Compiler (recently the HI-TECH Software has been acquired by Microchip). This C language version could be used for learning as well as the embedded system programming language comparison.

In this project I also use a new Microchip PICKit3 programmer but of course you could use the Microchip PICKit2 programmer to download the hex code to the PIC12F683 microcontroller flash.

Building your own Simple Laser Projector using the Microchip PIC12F683

The following is the Laser Projector code in PIC Assembly Language:

;******************************************************************************
;  File Name    : laserlight.asm
;  Version      : 1.0
;  Description  : Laser Light Show Project
;  Author       : RWB
;  Target       : Microchip PIC12F683 Microcontroller
;  Compiler     : Microchip Assembler (MPASMWIN.exe v5.35, mplink.exe v4.35)
;  IDE          : Microchip MPLAB IDE v8.46
;  Programmer   : PICKit3 (Firmware Suite 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 registers for the Delay and Delay + 1
mode                         ; Operation Mode
pwm_count                    ; Hold the Main PWM Counter
pwm_m1                       ; Hold the PWM width for Motor 1
pwm_m2                       ; Hold the PWM width for Motor 2
keycount                     ; Debounce Count
tableindex                   ; Table Index for Auto PWM
endc
; Define variable use for storing STATUS and WREG register
cblock 0x70             ; Use unbanked RAM, available both in Bank0 and Bank1
saved_w
saved_status
endc
; Start the Light show Assembler Code here
org 0x00                ; We always start at flash address 0
goto Main               ; Jump to Main
     org 0x04                ; 0x04: Start PIC Interrupt Address
PIC_ISR:                     ; Start the PIC Interrupt Service 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) then CheckPWM
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                  ; Return from Interrupt
Main:
bsf     STATUS,RP0      ; Select Registers at Bank 1
movlw   0x70
movwf   OSCCON          ; Set the internal clock speed to 8 MHz
movlw   0x39            ; GP1 and GP2 Output, GP0,GP3,GP4 and 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
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) goto KeyPressed
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 ADC peripheral, 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 ADC peripheral, 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 ADC peripheral, 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 ADC peripheral, 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 for PWM
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) then tableindex = 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: Millisecond Delay 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 the next instruction
goto    DelayLoop       ; Not zero goto DelayLoop
return                  ; return to the caller
;----------------- Delay1ms: 1 ms Delay Subroutine ---------------------------
Delay1ms:                    ; Total Delay: 1998 x 0.5us ~ 1 ms
movlw   0x99
movwf   Delay
DelayLoop1:
decfsz  Delay,f         ; Decrease Delay, If zero skip the next instruction
goto    DelayLoop1
DelayLoop2:
decfsz  Delay,f         ; Decrease Delay, If zero skip the next instruction
goto    DelayLoop2      ; Not zero goto DelayLoop2
DelayLoop3:
decfsz  Delay,f         ; Decrease Delay, If zero skip the next instruction
goto    DelayLoop3      ; Not zero goto DelayLoop2
return                  ; Return to the caller
     end
; EOF: laserlight.asm

The following is the Laser Projector Project code in C Language version:

// ***************************************************************************
//  File Name    : laserlight.c
//  Version      : 1.0
//  Description  : Laser Light Show Project
//  Author       : RWB
//  Target       : Microchip PIC12F683 Microcontroller
//  Compiler     : HI-TECH C PIC10/12/16 MCUs (Lite Mode) V9.70
//  IDE          : Microchip MPLAB IDE v8.46
//  Programmer   : PICKit3 (Firmware Suite Version: 01.25.20)
//  Last Updated : 03 April 2010
// ***************************************************************************
#include <pic.h>
/*   PIC Configuration Bit:
**   INTIO     - Using Internal RC No Clock
**   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 Detect Disable
**   IESODIS   - Internal External Switch Over Mode Disable
**   FCMDIS    - Monitor Clock Fail Safe 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 char tablepwm1[10]={0x10,0x5A,0x9A,0x20,0x40,0x8A,0x82,0x30,0x58,0xAA};
unsigned char tablepwm2[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 Count Increment
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;   // Initial Value for TIMER0 Interrupt
T0IF = 0;	       // Clear TIMER0 interrupt flag
}
}
void main(void)
{
unsigned char mode,keycount;
  OSCCON=0x70;         // Select 8 MHz internal clock
  /* Initial Port Used */
TRISIO = 0x39;       // GP1 and GP2 Output, GP0,GP3,GP4 and GP5 as Input
CMCON0 = 0x07;       // Turn off Comparator (GP0, GP1, GP2)
GPIO = 0x00;         // Turn Off all 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 off GP1
GPIO2=0;             // Turn off GP2
delay_ms(500);
}
}
}
    if (mode == 1) {
/* Read the ADC here */
ADCON0=0b00000001;       // select left justify result. ADC port channel AN0
GODONE=1;	               // initiate conversion on the channel 0
      while(GODONE) continue;  // Wait for ldr_left conversion done
pwm_m1=ADRESH;           // Read 8 bits MSB, Ignore 2 bits LSB in ADRESL
      delay_ms(1);
      /* Read the ADC here */
ADCON0=0b00001101;       // select left justify result. ADC port channel AN3
GODONE=1;	         // initiate conversion on the channel 4
      while(GODONE) continue;  // Wait for ldr_left conversion done
pwm_m2=ADRESH;           // Read 8 bits 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 */

Generating the PWM (Pulse Width Modulation)

READ  36V, 2-ch, 1.6A synchronous buck LED driver has I²C dimming

 

For more detail: Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller




Current Project / Post can also be found using:

  • circuit diagram of projector

Leave a Comment

*
= 4 + 1

Read previous post:
Night Light Saver V3.2 (PIC12C508)

A Microchip PIC12C508 8-pin CMOS Microcontroller turns a night light on and off with preset time everyday. Save Energy, No...

Close
Scroll to top