FB pixel

Creating a PWM in Assembly - Part 7 Microcontroller Basics (PIC10F200)

Published

Note: Microchip recently made some changes and their newer IDE versions use the XC8 compiler for Assembly, whereas all sample code here is created for the MPASM compiler. However, the sample code has been ported to XC8 by user tinyelect on our Discord channel (to whom we are extremely grateful!) and the XC8 version of the code is at the bottom of this tutorial for your reference. To see the changes needed to switch from MPASM to XC8, please check out the process that he shared.

Hi there and welcome back! In tutorial 6, we made an LED blink and this time, we’re going to control the LED again but, unlike last time, we will not just blink the LED but smoothly change its brightness.

First, at the end of the last chapter, I challenged you to figure out how to make both LED’s blink on and off, opposite to each other. I’ll show you my solution as there are some tricks you should know, and maybe you already figured them out when you tried to implement this by yourself. So, here we go:

#include "p10f200.inc"

; CONFIG

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

   ORG 0x0000

INIT

   MOVLW  ~(1<<T0CS) ;Enable GPIO2

   OPTION

   MOVLW ~((1 << GP2) | (1 << GP1));Set GP1 and GP2 as outputs

   TRIS GPIO

LOOP

   BSF GPIO, GP2                  ;Set GP2

   NOP   ;No operation

   BCF GPIO, GP1                  ;Reset GP1

   CALL DELAY     ;Call DELAY subroutine

   BCF GPIO, GP2       ;Reset GP2

   NOP   ;No operation

   BSF GPIO, GP1               ;Set GP1

   CALL DELAY     ;Call DELAY subroutine

   GOTO LOOP                 ;loop forever

DELAY    ;Start DELAY subroutine here

    MOVLW 5                            ;Load initial value for the delay    

    MOVWF 10   ;Copy the value to the register 0x10

    MOVWF 11   ;Copy the value to the register 0x11

    MOVWF 12   ;Copy the value to the register 0x12

DELAY_LOOP   ;Start delay loop

    DECFSZ 10, F               ;Decrement the register 0x10 and check if not zero

    GOTO DELAY_LOOP   ;If not then go to the DELAY_LOOP label

    DECFSZ 11, F               ;Else decrement the register 0x11, check if it is not 0

    GOTO DELAY_LOOP   ;If not then go to the DELAY_LOOP label

    DECFSZ 12, F               ;Else decrement the register 0x12, check if it is not 0

    GOTO DELAY_LOOP   ;If not then go to the DELAY_LOOP label

    RETLW 0   ;Else return from the subroutine


    END

As you probably guessed, to blink the LEDs in opposite you need set GP2 and clear GP1 simultaneously, and then clear GP2 and set GP1 also simultaneously. The first case is implemented in lines 11 and 13, and the second case is done in lines 15 and 17.

You may notice that between these pairs of lines I inserted an unknown instruction “NOP” in lines 12 and 16. What is that and why is it needed here?

As for the first question, the NOP instruction means “No OPeration”, so the microcontroller does nothing while performing it. Why is it needed, then? Sometimes we need to make very short delays, for 1 or 2 cycles. In this case we don’t need to invent something random for the microcontroller to do in this period, we can just use the NOP instruction.

Now, why would I have these NOPs in the code? BCF and BSF instructions belong to the so-called “read-modify-write” type. That means that to change the bit value the microprocessor reads the entire register, modifies a single bit in it (set or clears) and writes it back. It is normal to use two such sequential operations in almost all cases except one - when it is applied to the GPIO register. What is wrong with it?

In line 11 with the instruction BSF GPIO, GP2 we set the GP2 bit as 1. But, in fact the GP2 output is latched *after* this instruction is implemented, we just gave the command to set the output pin, it hasn’t actually happened yet. Let’s imagine we invoke the BCF GPIO, GP1 instruction right after the previous one. The BCF instruction reads the actual value of the pins, and that means that GP2 output may be still in the low state, not high. Thus, this instruction will discard the previous one, and GP2 will remain low. But it may also be OK, so the behavior is undefined - you don’t actually know what is going to happen. To make the behavior more predictable, we use the NOP instruction, which gives it enough time to consistently latch the output pin before reading it for the next instruction. You can read more about it in chapter 5.4 of the data sheet of the PIC10F200 microcontroller.

I also made a change in the DELAY subroutine. You may notice that I use three registers now, because we need to make 1 second delay and, with the two registers, the maximum delay is just 200ms. In line 22, we load the value 5 into all the registers as the initial value.

According to the equation I provided last time, the approximate delay will be 5 x 256 x 256 x 3 = 983040us, which is quite close to 1s.

That’s it about the challenge from Tutorial 6, let’s now switch to the current task, fading the brightness of LED1 in.

PIC10F200 Pulse Width Modulation (PWM)

As you know (and if you don’t please see this tutorial) the brightness of an LED depends on the amount of current flowing through it. The higher the current, the brighter LED shines. But it’s quite hard to change the current directly with the PIC10F200 microcontroller as it doesn’t have a DAC. So we will use another way, which is extremely widespread and used in almost all dimming systems. This is, of course, pulse width modulation (PWM). I won’t go over it in detail as there are tons of articles about PWM in the internet, including here on CircuitBread.com, so if this is the first time you’ve ever heard about it, go check it out.

Briefly, we can control brightness by means of the PWM by switching the LED on and off many times a second. The switching is so fast that our eyes can’t see the flicker, and considers the LED to be always on. With the PWM, we change the LED on/off ratio - the duty cycle - and this changes the brightness of the LED.

So, by changing the duty cycle of the PWM, we will smoothly adjust the brightness from 0 to full brightness before it drops immediately back to darkness before coming up to full brightness again. Let’s now consider the code that implements the given task:

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

    SUBWF i, W   ;Subtract the W from i

    BTFSS STATUS, Z   ;If the result is not 0

    GOTO $ + 2   ;then go to the line 22

    BCF GPIO, GP1               ;else reset the GP1 pin

    CALL DELAY   ;and call the DELAY subroutine

    DECFSZ i, F   ;Decrement the i, and check if the result is 0

    GOTO INT_LOOP    ;If not, return to the internal PWM loop start

    DECF limit, F              ;otherwise change the PWM limit

    GOTO LOOP   ;and return to the main loop

DELAY   ;Start DELAY subroutine here

    MOVLW 10   ;Load initial value for the delay    

    MOVWF j   ;Copy the value to j

DELAY_LOOP    ;Start delay loop

    DECFSZ j, F   ;Decrement j and check if it is not zero

    GOTO DELAY_LOOP   ;If not, then go to the DELAY_LOOP label

    RETLW 0   ;Else return from the subroutine

    END

Our programs are becoming longer and longer, but, at the same time, there are fewer new instructions each time.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

In lines 4, 5, 6 there is new Assembly directive EQU. It sounds like the beginning of the word “EQUivalent” which, basically, is what it means. This directive defines the constant and sets its value. Actually, you could use the #define directive like in C language but EQU looks more “assemblish”. “Assembly-ish”? Either way. So we define three constants - “i”, “limit”, and “j”, and set their values as 0x10, 0x11, and 0x12 correspondingly. We do this to give the names to specific parts of the unnamed general purpose register. It’s difficult to remember what is stored in register 0x10 if the program is big, but, by this directive, we know that 0x10 is called “i” which sounds like a loop variable (which it is). The same with “j”. The “limit” register will consist of the PWM limit value, the target value at which point the pulse will end.

Please note that the name given by the EQU directive should be written in the first column like a label.

Ideally, now lines from 1 to 10 should be clear to you.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

In line 11 there is new instruction CLRF (CLeaR File register). As follows from its name, it clears the mentioned file register, which means the register value after implementing this instruction is zero. In line 11 we clear the “limit” register to start with the LED at full brightness. Give me a moment and I’ll explain how this works soon.

In lines 13 and 14 we load the value 0xFF into the “i” register.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

In line 15 we set GP1 “high”.

Let’s first consider the whole operation of the PWM before jumping into the internal loop. So we preloaded the “i” register with the value 0xFF, or decimal 255. In the internal loop, we will decrement the “i” register on each iteration and compare it with the “limit” register. If “limit” is equal to the “i” then we clear the GP1 pin and keep decrementing “i” until it becomes 0. As soon as this happens we exit the internal loop, preload the “i” register with 0xFF and set the GP1 pin again.

Clearing a pin is a "0" and sets the voltage low while setting a pin is a "1" and sets the voltage high.

Thus, the smaller the value in the “limit” register is, the longer the LED will stay on during the period, and the brighter the LED will be. That’s why the value “0” of the “limit” corresponds to the maximum brightness and 255 corresponds to completely off.

Also, we decrement the value of “limit” each iteration of the main loop to make the brightness consistently higher. When “limit” becomes 0, on the next decrement it will become 255, which means that after it is fully bright, it will immediately start again from darkness. So let’s figure out how this is done in our program.

As mentioned in Tutorial 6, in an 8-bit microcontroller, 255 + 1 = 0 and 0 - 1 = 255.

Unfortunately, in Assembly language you can’t simply write “if (i == limit)”, there is no instruction for it, so we need to perform the sequence of operations to implement this simple comparison.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

Line 16 is the start of the internal loop, in which we will decrement the “i” value from 255 to 0.

In line 17 there is another new instruction MOVF (MOVe File register). Don’t mix this up with MOVWF, they both move things and the “W” register is involved but they’re very different. The MOVF instruction moves the file register mentioned as operand 1 to the W register if operand 2 is W or back to the same file register if operand 2 is F. So by writing MOVF limit, W we copy the value of the “limit” register to the W register. We have to do this because we can’t perform arithmetic operations between two file registers, only between one file register and W register.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

    SUBWF i, W   ;Subtract the W from i

Line 18 consists of another new instruction SUBWF (SUBtract W from File register). This should be easier to understand from its name. It subtracts the value in the W register from the file register mentioned as operand 1 and saves the result into the file register or W register depending on the operand 2. So, by invoking SUBWF i, W we subtract the W register from “i” (and we remember that it is equal to the “limit” because of the previous line) and save the result back to the W register. Actually we are not interested in the result itself, we just need to know if it is 0 (which means that “i” = “limit”). That’s why we store the result in the auxiliary register W. The SUBWF instruction also affects certain bits of the STATUS register (C and Z in particular). But now we are interested just in the Z bit. As I mentioned previously, it is set to 1 when the result of the previous operation was zero, otherwise it is 0.

In the status register, C is the "Carry bit" and Z is the "Zero bit".

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

    SUBWF i, W   ;Subtract the W from i

    BTFSS STATUS, Z   ;If the result is not 0

In line 19 we find another new instruction BTFSS (Bit Test in File register, Skip if Set). It tests the bit given as operand 2 in the file register given as the operand 1, and if the bit is set, the next line is skipped. So this is another conditional branch instruction, in addition to DECFSZ we already know.

By calling BTFSS STATUS, Z we check if the Z bit of the STATUS register is set (basically if “i” = “limit”). And if it is, we skip line 20 and go to line 21. Otherwise, it runs line 20.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

    SUBWF i, W   ;Subtract the W from i

    BTFSS STATUS, Z   ;If the result is not 0

    GOTO $ + 2   ;then go to the line 22

In line 20 we see a familiar instruction (finally!) - GOTO. But with an unknown operand (ugh!). The $ sign means the current PC value.

Reminder: I know we keep reminding you of this, but PC is the program counter.

If you write “GOTO $” this means that you load the current address into the PC register, and this line is invoked again and again, implementing endless loop (generally considered bad). If you write “GOTO $ + 1” this will load the PC with the address of the next line.

And finally, the expression “GOTO $ + 2” will load the PC with the address of the line after the next, in our case, it is line 22.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

    SUBWF i, W   ;Subtract the W from i

    BTFSS STATUS, Z   ;If the result is not 0

    GOTO $ + 2   ;then go to the line 22

    BCF GPIO, GP1               ;else reset the GP1 pin

    CALL DELAY   ;and call the DELAY subroutine

    DECFSZ i, F   ;Decrement the i, and check if the result is 0

In line 21 (and remember that it is implemented only when “i” = “limit”) we turn off the LED by the well-known instruction BCF.

In line 22 we call the DELAY subroutine. We need the delay to slow down changing the brightness. If we don’t use it, it will change very fast. On the other hand, the delay shouldn’t be very big, otherwise you will notice the LED flicker.

In line 23 we use the operator DECFSZ to decrement the “i” value and check if it is now 0. If it isn’t, then line 24 is implemented.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

    SUBWF i, W   ;Subtract the W from i

    BTFSS STATUS, Z   ;If the result is not 0

    GOTO $ + 2   ;then go to the line 22

    BCF GPIO, GP1               ;else reset the GP1 pin

    CALL DELAY   ;and call the DELAY subroutine

    DECFSZ i, F   ;Decrement the i, and check if the result is 0

    GOTO INT_LOOP    ;If not, return to the internal PWM loop start

    DECF limit, F              ;otherwise change the PWM limit

    GOTO LOOP   ;and return to the main loop

At line 24 the PC returns to the INT_LOOP label with the GOTO instruction. If the value of “i” is 0, then line 24 is skipped.

If “i” is not 0, line 25 is implemented where we see another new instruction DECF (DECrement File register). This one is similar to DECFSZ but is simpler - it doesn’t test the result, it just decrements the value of the file register.

In line 26, the value of the “limit” register is decremented to increase the brightness in the next iteration. Please note that line 25 doesn’t belong to the internal loop, it is outside it, and belongs to the main loop.

Line 26 loads the PC with the value under the LOOP label with the GOTO instruction, and this line ends both the main loop and the main program.

#include "p10f200.inc"

   __CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i   EQU   10   ;define 0x10 register as the PWM loop variable

limit   EQU   11   ;define 0x11 register as the PWM limit

j    EQU   12   ;define 0x12 register as the delay variable

ORG 0x0000    

INIT

MOVLW ~(1 << GP1)          ;set GP1 as an output

    TRIS GPIO    

    CLRF limit    ;Clear the PWM limit

LOOP

    MOVLW 0xFF   ;Set the initial value of i

    MOVWF i    ;as 0xFF

    BSF GPIO, GP1      ;Set GP1 pin

INT_LOOP   ;Beginning of the internal PWM loop

    MOVF limit, W               ;Copy the PWM limit value of the W register

    SUBWF i, W   ;Subtract the W from i

    BTFSS STATUS, Z   ;If the result is not 0

    GOTO $ + 2   ;then go to the line 22

    BCF GPIO, GP1               ;else reset the GP1 pin

    CALL DELAY   ;and call the DELAY subroutine

    DECFSZ i, F   ;Decrement the i, and check if the result is 0

    GOTO INT_LOOP    ;If not, return to the internal PWM loop start

    DECF limit, F              ;otherwise change the PWM limit

    GOTO LOOP   ;and return to the main loop

DELAY   ;Start DELAY subroutine here

    MOVLW 10   ;Load initial value for the delay    

    MOVWF j   ;Copy the value to j

DELAY_LOOP    ;Start delay loop

    DECFSZ j, F   ;Decrement j and check if it is not zero

    GOTO DELAY_LOOP   ;If not, then go to the DELAY_LOOP label

    RETLW 0   ;Else return from the subroutine

    END

Lines 28 to 36 implement the DELAY subroutine. Unlike the previous program, here we use only one register to decrement - “j”. This is because we don’t need big delays. In line 29 we load the initial value 0x10 (which is 16 in decimal system) thus, total delay is 16 x 3*(DELAY LOOP) + 1*(MOVLW) + 1*(MOVWF) + 2*(GOTO) + 2*(RETLW) + 2*(CALL) = 56us.

I recommend you change the “j” value and see how this will affect program execution.


And that’s all, folks! Now let’s assemble the program and load it into the microcontroller to enjoy LED1 flashing. As I mentioned, you may try to change the delay ‘j’ to see how it will affect the flashing. Also, you can change the “i” and “limit” values to see their influence and get a more intuitive understanding of how this works.

I have a very useful device called a Logic Analyzer. If you are going to seriously work with microcontrollers this tool is absolutely a must-have. I bought the clone of the Saleae Logic logic analyzer on Aliexpress for about $5, and I still consider it as my best buy.

Anyway, I’m sharing this not to boast but to show you what’s going on with the GP1 pin when the program is executed (see figure 1).

Figure 1 - Program Implementation
Figure 1 - Program Implementation

Looks a bit boring, huh? That’s because the step is too small and the delay is relatively big.

I will change the program to fix these two flaws by removing the extra delay and reducing the “limit” by 16 for each iteration, not 1 (Take a moment to think about how this works). And after that, the results are much more obvious (see figure 2).

Figure 2 - Adapted Program Execution
Figure 2 - Adapted Program Execution

In figure 2 you can clearly see the changing pulse width, which means that our program works as desired.

And, as I like to do, let’s go over some statistics from this tutorial. The code size that implements this task is 21 words, so still less than 1/10th of our full memory space. We’ve learned another 6 new instructions - NOP, CLRF, MOVF, SUBWF, BTFSS, and DECF, so with the previous 10 we know already 16 instructions, almost half! Also, we’ve learned the new Assembly directive EQU which can help make the code more readable.

To practice and see if you have a good grasp of what I’ve gone over in this tutorial, I recommend you change the code so the brightness of the LED will smoothly change from the minimum to maximum brightness, and then smoothly from max back down to minimum. I think to make this task you might need the instruction INCF (INCrement File register) which is the same concept as DECF but the opposite direction. It increments the value of the given file register. As usual I will provide my solution in the beginning of the next tutorial. Good luck!

More Microcontroller Tutorials here.



Homework Solution. However don't read it until you're done with your code!

#include "p10f200.inc"

__CONFIG _WDT_OFF & _CP_OFF & _MCLRE_OFF

i EQU 10

limit EQU 11

j EQU 12

dir EQU 13

ORG 0x0000

INIT

MOVLW ~(1 << GP1)

TRIS GPIO

CLRF limit

CLRF dir

LOOP

MOVLW 0xFF

MOVWF i

BSF GPIO, GP1

INT_LOOP

MOVF limit, W

SUBWF i, W

BTFSS STATUS, Z

GOTO $ + 2

BCF GPIO, GP1

CALL DELAY

DECFSZ i, F

GOTO INT_LOOP

BTFSS dir, 0

GOTO DEC_BRIGHTNESS

DECFSZ limit, F

GOTO LOOP

GOTO TOGGLE_DIR

DEC_BRIGHTNESS

INCF limit, F

MOVLW 0xFF

SUBWF limit, W

BTFSS STATUS, Z

GOTO LOOP

TOGGLE_DIR

BTFSS dir, 0

GOTO SET_DIR

BCF dir, 0

GOTO LOOP

SET_DIR

BSF dir, 0

GOTO LOOP

DELAY

MOVLW 10

MOVWF j

DELAY_LOOP

DECFSZ j, F

GOTO DELAY_LOOP

RETLW 0

END

The comments and explanations are at the beginning of the next tutorial.

Make Bread with our CircuitBread Toaster!

Get the latest tools and tutorials, fresh from the toaster.

What are you looking for?