LED FLASHING RATE CONTROL USING PIC16F684

JohnInTX

Joined Jun 26, 2012
4,787
You have two pots so you have two analog inputs. Assuming they are connected to AN0 and AN1, you must set the corresponding bits in ANSEL to '1' , the corresponding TRIS bits also to '1' and as @AlbertHall said (while I was typing) select between your two analog channels i.e. select AN0, convert, save the result then select AN1, convert again, this time on channel 1 and save that result in a different variable. Then you take the two results, rate and brightness and do your delay flash and PWM processing from there. Channels are selected in ADCON0.
 

MaxHeadRoom

Joined Jul 18, 2013
30,665
@Don Omar if you intend pursuing Pic's in the future using assembly programming I would really recommend switching to the 18F series, for a much enhanced instruction set and more modules.
Max.
 

JohnInTX

Joined Jun 26, 2012
4,787
+1 And while we are at it
bsf TRISA ^ 0x80, 0 ; enable input on pin A0
is a bizzare construct. Its purpose is to make bit 7 of the address of TRISA a '0' to avoid the warning:
Message [302] Register in operand not in bank 0. Ensure that bank bits are correct.
This is indeed a useless message but the best way to deal with it is to use the directive:
ERRORLEVEL -302
at the start of the program to suppress the message. Buggering up the address - particularly with XOR (!) - makes assumptions about how the assembler does address reductions and that is a great way to get hammered. If, as @MaxHeadRoom recommends, you were to port your code to another PIC series or possibly a different version of MPASM, you would find constructs such as those endlessly problematic.

Just trying to keep you out of trouble..
 

Thread Starter

Don Omar

Joined May 5, 2017
49
You have two pots so you have two analog inputs. Assuming they are connected to AN0 and AN1, you must set the corresponding bits in ANSEL to '1' , the corresponding TRIS bits also to '1' and as @AlbertHall said (while I was typing) select between your two analog channels i.e. select AN0, convert, save the result then select AN1, convert again, this time on channel 1 and save that result in a different variable. Then you take the two results, rate and brightness and do your delay flash and PWM processing from there. Channels are selected in ADCON0.

this is me trying to change from AN0 to AN1 so i changed both the Ansel and tris to 1 still only AN0 works,im really confused i did the same with the one on the datasheet and the code that dodydave give me
upload_2017-5-9_2-29-28.png
 

JohnInTX

Joined Jun 26, 2012
4,787
@Don Omar
Post the code as a text file, not a screen shot so we can see the whole thing and load it into MPLAB.

You show only one input being configured. Remember, you configure all of the analog inputs once then select the channel to convert using ADCON0. While you are at it, it is best to configure ALL bits in ALL ports by loading W and using movwf to write all the bits. Its not a good idea to have unconfigured port bits. Set all unused pins to output 0.

EDIT: FWIW, I added to your main loop to show one way of doing both channels. I don't like using dumb delays but one thing at a time. I broke the flash delay down into 1ms per loop segments to avoid bogging down with slow flashes. Note how I get the ADC result then change to the next channel before processing the current result to let the ADC settle while processing.

The logic to set the lower bits of the PWM was wrong. I show one way to do it. Note that neither of our methods is synced to the PWM but for the lower 2 bits it probably doesn't matter. I assume that the PWM is to be used for dimming, not flashing. In that case, 244Hz might be a little slow flicker wise.

Flashing the LED can be done by either switching the PWM on and off OR driving the anode of the LED from the PWM and cathode from another output port. '0' at the port lets the PWM drive the LED. '1' at the port turns the LED off. A schematic of your board would help here.

I didn't clean up the goofy ^80h stuff, I just suppressed the messages. I also didn't fully configure the IO cuz it is late. Its not fully tested either but should give you an idea of where to go from here..

The rest of the config stuff looks right, delay counts look OK according to the stopwatch and your PWM settings agree with the 'lil Professor so have at it.

Good luck!
Code:
  TITLE "pwm_led.asm" ;
  List P=PIC16F684, R=DEC
  INCLUDE "p16f684.inc"

  ERRORLEVEL -302  ; suppress 'not in bank' messages

  ; data segment
  CBLOCK 0x20
  del  ; variable used for delay
  temp  ; local temp variable
  FlashK  ; counts 1 ms delays to do flash
  ENDC

  ; code segment
  PAGE
  __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

  ;****************** PROGRAM BEGINS  ******************************

  ORG 0 ; start program at the beginning of mem

  bcf STATUS, RP0 ; activate BANK 0
  clrf PORTA ; initialize PORT A
  clrf PORTC ; initialize PORT C
  movlw 0x07
  movwf CMCON0 ; comparators OFF
  bsf STATUS, RP0 ; change to BANK 1

  ; ADC and PWM configuration
  bsf TRISA ^ 0x80, 0 ; enable input on pin A0
  bsf ANSEL ^ 0x80, 0 ; configure A0 as analog input
  movlw b'01110000' ; set ADC Frc clock
  movwf ADCON1 ^ 0x80
  movlw 0xFF
  movwf PR2 ^ 0x80 ; PWM period 244Hz

  bcf TRISC ^ 0x80, 5 ; enable RC5 for output
  bcf STATUS, RP0 ; back to BANK 0

  bsf ADCON0, 0 ; Left justify, Vdd Vref, AN0, On
  movlw 7
  movwf T2CON ; enable Timer 2 with 1:16 prescaler
  movlw 0x0C ; enable single output PWM
  movwf CCP1CON

loop:
  ;-------------------- PROCESS AN0 -> PWM  ------------------
  ; PWM controls brightness
  ; Assumes channel 0 is set
  bsf ADCON0, GO  ; start ADC operation
  btfsc ADCON0, GO  ; and wait for its completion
  goto $-1

  bsf ADCON0,CHS0  ; select channel 1 for NEXT convert, to let it settle
  ; then process channel 0 result (PWM reading)

  bsf STATUS, RP0  ; change to BANK 1
  movf ADRESL,W  ; read 2 lower bits of ADC
  bcf STATUS, RP0
  movwf temp  ; save for shifts
  rrf temp, f  ; shift one right
  rrf temp, W  ; shift one right, dest is W
  andlw  B'00110000' ; strip other bits
  bcf CCP1CON,5  ; clear CCP1CON 5:4 without affecting W
  bcf CCP1CON,4
  iorwf  CCP1CON,F  ; combine temp >> 2 with CCP1CON 5:4

  movf ADRESH, W  ; get high 8 bits of ADC
  movwf CCPR1L  ; setup PWM duty cycle

  ;----------------- PROCESS AN1 -> FLASH RATE  ------------------
  ; Toggles LED every FlashK milliseconds, updates rate at end of each
  ;  flash 1/2 cycle.
  ; assumes AN1 is set but converts only when current LED flash is
  ;  done i.e. FlashK decrements to zero.
  ;  When FlashK==0, gets new FlashK from ADC to change rate
  ; Total flash time is broken into 1ms increments so that it does
  ;  not bog down the PWM sampling for slow flash rates.

  decfsz  FlashK,F  ; flash msecs elapsed?
  goto  _flash1ms  ; nope, wait another ms towards finishing this flash

  ; else, get new FlashK from ADC
  bsf ADCON0, GO  ; start ADC operation
  btfsc ADCON0, GO  ; and wait for its completion
  goto $-1

  movf  ADRESH,W  ; get result
  movwf  FlashK  ; to flash counter
  ;----------------
  ; toggle LED here
  ;----------------

_flash1ms:
  bcf ADCON0,CHS0  ; select channel 0 for NEXT convert, let it settle

  movlw  1  ; delay 1 ms per loop
  movwf  del
  call  delay

  goto loop ; endless loop

  ;**************** UTILITIES  ****************************
  ; procedures
delay ; a delay for del milliseconds
  movlw 200

  sublw 1 ; this loop takes 5us*200 = 1ms
  sublw 0 ; for PIC16F684 @ 4 MHz
  btfss STATUS, Z
  goto $-3

  decfsz del, f
  goto delay
  return

  END
 
Last edited:

Thread Starter

Don Omar

Joined May 5, 2017
49
@Don Omar
Post the code as a text file, not a screen shot so we can see the whole thing and load it into MPLAB.

You show only one input being configured. Remember, you configure all of the analog inputs once then select the channel to convert using ADCON0. While you are at it, it is best to configure ALL bits in ALL ports by loading W and using movwf to write all the bits. Its not a good idea to have unconfigured port bits. Set all unused pins to output 0.

EDIT: FWIW, I added to your main loop to show one way of doing both channels. I don't like using dumb delays but one thing at a time. I broke the flash delay down into 1ms per loop segments to avoid bogging down with slow flashes. Note how I get the ADC result then change to the next channel before processing the current result to let the ADC settle while processing.

The logic to set the lower bits of the PWM was wrong. I show one way to do it. Note that neither of our methods is synced to the PWM but for the lower 2 bits it probably doesn't matter. I assume that the PWM is to be used for dimming, not flashing. In that case, 244Hz might be a little slow flicker wise.

Flashing the LED can be done by either switching the PWM on and off OR driving the anode of the LED from the PWM and cathode from another output port. '0' at the port lets the PWM drive the LED. '1' at the port turns the LED off. A schematic of your board would help here.

I didn't clean up the goofy ^80h stuff, I just suppressed the messages. I also didn't fully configure the IO cuz it is late. Its not fully tested either but should give you an idea of where to go from here..

The rest of the config stuff looks right, delay counts look OK according to the stopwatch and your PWM settings agree with the 'lil Professor so have at it.

Good luck!
Code:
  TITLE "pwm_led.asm" ;
  List P=PIC16F684, R=DEC
  INCLUDE "p16f684.inc"

  ERRORLEVEL -302  ; suppress 'not in bank' messages

  ; data segment
  CBLOCK 0x20
  del  ; variable used for delay
  temp  ; local temp variable
  FlashK  ; counts 1 ms delays to do flash
  ENDC

  ; code segment
  PAGE
  __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

  ;****************** PROGRAM BEGINS  ******************************

  ORG 0 ; start program at the beginning of mem

  bcf STATUS, RP0 ; activate BANK 0
  clrf PORTA ; initialize PORT A
  clrf PORTC ; initialize PORT C
  movlw 0x07
  movwf CMCON0 ; comparators OFF
  bsf STATUS, RP0 ; change to BANK 1

  ; ADC and PWM configuration
  bsf TRISA ^ 0x80, 0 ; enable input on pin A0
  bsf ANSEL ^ 0x80, 0 ; configure A0 as analog input
  movlw b'01110000' ; set ADC Frc clock
  movwf ADCON1 ^ 0x80
  movlw 0xFF
  movwf PR2 ^ 0x80 ; PWM period 244Hz

  bcf TRISC ^ 0x80, 5 ; enable RC5 for output
  bcf STATUS, RP0 ; back to BANK 0

  bsf ADCON0, 0 ; Left justify, Vdd Vref, AN0, On
  movlw 7
  movwf T2CON ; enable Timer 2 with 1:16 prescaler
  movlw 0x0C ; enable single output PWM
  movwf CCP1CON

loop:
  ;-------------------- PROCESS AN0 -> PWM  ------------------
  ; PWM controls brightness
  ; Assumes channel 0 is set
  bsf ADCON0, GO  ; start ADC operation
  btfsc ADCON0, GO  ; and wait for its completion
  goto $-1

  bsf ADCON0,CHS0  ; select channel 1 for NEXT convert, to let it settle
  ; then process channel 0 result (PWM reading)

  bsf STATUS, RP0  ; change to BANK 1
  movf ADRESL,W  ; read 2 lower bits of ADC
  bcf STATUS, RP0
  movwf temp  ; save for shifts
  rrf temp, f  ; shift one right
  rrf temp, W  ; shift one right, dest is W
  andlw  B'00110000' ; strip other bits
  bcf CCP1CON,5  ; clear CCP1CON 5:4 without affecting W
  bcf CCP1CON,4
  iorwf  CCP1CON,F  ; combine temp >> 2 with CCP1CON 5:4

  movf ADRESH, W  ; get high 8 bits of ADC
  movwf CCPR1L  ; setup PWM duty cycle

  ;----------------- PROCESS AN1 -> FLASH RATE  ------------------
  ; Toggles LED every FlashK milliseconds, updates rate at end of each
  ;  flash 1/2 cycle.
  ; assumes AN1 is set but converts only when current LED flash is
  ;  done i.e. FlashK decrements to zero.
  ;  When FlashK==0, gets new FlashK from ADC to change rate
  ; Total flash time is broken into 1ms increments so that it does
  ;  not bog down the PWM sampling for slow flash rates.

  decfsz  FlashK,F  ; flash msecs elapsed?
  goto  _flash1ms  ; nope, wait another ms towards finishing this flash

  ; else, get new FlashK from ADC
  bsf ADCON0, GO  ; start ADC operation
  btfsc ADCON0, GO  ; and wait for its completion
  goto $-1

  movf  ADRESH,W  ; get result
  movwf  FlashK  ; to flash counter
  ;----------------
  ; toggle LED here
  ;----------------

_flash1ms:
  bcf ADCON0,CHS0  ; select channel 0 for NEXT convert, let it settle

  movlw  1  ; delay 1 ms per loop
  movwf  del
  call  delay

  goto loop ; endless loop

  ;**************** UTILITIES  ****************************
  ; procedures
delay ; a delay for del milliseconds
  movlw 200

  sublw 1 ; this loop takes 5us*200 = 1ms
  sublw 0 ; for PIC16F684 @ 4 MHz
  btfss STATUS, Z
  goto $-3

  decfsz del, f
  goto delay
  return

  END

@Johnlntx

this is what i did
seems t be not working for me or properly i did something wrong i really appreciate your help

Code:
TITLE "pwm_led.asm" ;
  List P=PIC16F684, R=DEC
  INCLUDE "p16f684.inc"

  ERRORLEVEL -302  ; suppress 'not in bank' messages

  ; data segment
  CBLOCK 0x20
  del  ; variable used for delay
  temp  ; local temp variable
  FlashK  ; counts 1 ms delays to do flash
  ENDC

  ; code segment
  PAGE
  __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

  ;****************** PROGRAM BEGINS  ******************************

  ORG 0 ; start program at the beginning of mem

  ;Init
   clrf   PORTA          ; initialize PORT A
   clrf   PORTC          ; initialize PORT C
   bsf    STATUS,RP0     ; select memory bank
   movlw   B'00001111'   ;
   movwf   TRISA         ;
   movlw   B'11100000'   ;
   movwf   TRISC         ;
   movlw 0x07
   movwf CMCON0 ; comparators OFF
   bsf STATUS, RP0 ; change to BANK 1

; ADC and PWM configuration
   movlw   B'00001111'   ; AN0 to AN3 are analog inputs
   movwf   ANSEL          ;Bank 1
   movlw   B'01000000'   ; left justified A/D result, VCC as ref
   movwf   ADCON1        ;Bank 1
   movlw b'01110000'     ; set ADC Frc clock
   movwf ADCON1
   movlw 0xFF
   movwf PR2  ; PWM period 244Hz
   bcf TRISC , 5 ; enable RC5 for output
   bcf STATUS, RP0 ; back to BANK 0
   bsf ADCON0, 0 ; Left justify, Vdd Vref, AN0, On
   movlw 7
   movwf T2CON ; enable Timer 2 with 1:16 prescaler
   movlw 0x0C ; enable single output PWM
   movwf CCP1CON

loop
  ;-------------------- PROCESS AN0 -> PWM  ------------------
  ; PWM controls brightness
  ; Assumes channel 0 is set
  bsf ADCON0,   GO  ; start ADC operation
  btfsc ADCON0, GO  ; and wait for its completion
  goto $-1
  bsf ADCON0,CHS0  ; select channel 1 for NEXT convert, to let it settle
                   ; then process channel 0 result (PWM reading)

  bsf STATUS, RP0  ; change to BANK 1
  movf ADRESL,W    ; read 2 lower bits of ADC
  bcf STATUS, RP0
  movwf temp          ; save for shifts
  rrf temp, f         ; shift one right
  rrf temp, W         ; shift one right, dest is W
  andlw  B'00110000' ; strip other bits
  bcf CCP1CON,5       ; clear CCP1CON 5:4 without affecting W
  bcf CCP1CON,4
  iorwf  CCP1CON,F  ; combine temp >> 2 with CCP1CON 5:4

  movf ADRESH, W  ; get high 8 bits of ADC
  movwf CCPR1L  ; setup PWM duty cycle

  ;----------------- PROCESS AN1 -> FLASH RATE  ------------------
  ; Toggles LED every FlashK milliseconds, updates rate at end of each
  ;  flash 1/2 cycle.
  ; assumes AN1 is set but converts only when current LED flash is
  ;  done i.e. FlashK decrements to zero.
  ;  When FlashK==0, gets new FlashK from ADC to change rate
  ; Total flash time is broken into 1ms increments so that it does
  ;  not bog down the PWM sampling for slow flash rates.

  decfsz  FlashK,F  ; flash msecs elapsed?
  goto  _flash1ms  ; nope, wait another ms towards finishing this flash

  ; else, get new FlashK from ADC
  bsf ADCON0, GO  ; start ADC operation
  btfsc ADCON0, GO  ; and wait for its completion
  goto $-1

  movf  ADRESH,W  ; get result
  movwf  FlashK  ; to flash counter
  ;----------------
  ; toggle LED here
  ;----------------

_flash1ms
  bcf ADCON0,CHS0  ; select channel 0 for NEXT convert, let it settle
  movlw  1  ; delay 1 ms per loop
  movwf  del
  call  delay

  goto loop ; endless loop

  ;**************** UTILITIES  ****************************
  ; procedures
delay ; a delay for del milliseconds
  movlw 200

  sublw 1 ; this loop takes 5us*200 = 1ms
  sublw 0 ; for PIC16F684 @ 4 MHz
  btfss STATUS, Z
  goto $-3

  decfsz del, f
  goto delay
  return

  END
 
Last edited by a moderator:

JohnInTX

Joined Jun 26, 2012
4,787
What part is not working? There are lots of parts. Note that the IO code to switch the LED on/off for flashing is not implemented.
 

Thread Starter

Don Omar

Joined May 5, 2017
49
What part is not working? There are lots of parts. Note that the IO code to switch the LED on/off for flashing is not implemented.
I meant the brightness control for AN0 is working but I don't get anything from AN1 for flashing rate
Does it look alright ton you that's code I sent you
 

JohnInTX

Joined Jun 26, 2012
4,787
I meant the brightness control for AN0 is working but I don't get anything from AN1 for flashing rate
Does it look alright ton you that's code I sent you
See my post 27. It does not flash the LED because, while I showed a method to create the flasher, I did not add any code to actually do the flashing. There is a placeholder in the code at line 91 to show where the code could go but how to do it is left up to you. My approach may or may not be what you want. You should print the code and trace through it to understand it.

In post 25, I noted a couple of options:
Flashing the LED can be done by either switching the PWM on and off OR driving the anode of the LED from the PWM and cathode from another output port. '0' at the port lets the PWM drive the LED. '1' at the port turns the LED off. A schematic of your board would help here.
 

Thread Starter

Don Omar

Joined May 5, 2017
49
See my post 27. It does not flash the LED because, while I showed a method to create the flasher, I did not add any code to actually do the flashing. There is a placeholder in the code at line 91 to show where the code could go but how to do it is left up to you. My approach may or may not be what you want. You should print the code and trace through it to understand it.

In post 25, I noted a couple of options:
i have no idea how to do it
 

JohnInTX

Joined Jun 26, 2012
4,787
You want to flash the same LED that you are controlling brightness on yes?
Look at lines 72-102 in my post above. That generates the flash timing.
 

Thread Starter

Don Omar

Joined May 5, 2017
49
You want to flash the same LED that you are controlling brightness on yes?
Look at lines 72-102 in my post above. That generates the flash timing.

this is the code i used to control the flashing rate before
how can i add it to do one i have far, again this is controlling the led from pot AN0
Code:
LIST P=PIC16F684
include P16f684.inc

;data
CBLOCK 0x20
RESULTLO
RESULTHI
ENDC

__CONFIG _CP_OFF & _WDT_OFF & _BOD_OFF & _PWRTE_OFF & _INTRC_OSC_NOCLKOUT & _CPD_OFF

org 0x00
reset:
goto start

org 0x04
start:

bcf STATUS, RP0 ;Bank 0
bcf STATUS, RP1
clrf PORTC ; Clear PORTC
bsf STATUS, RP0

clrf TRISC ;PORTC is OUTPUT
bcf STATUS, RP0

loop:
call AD
bsf PORTC,5  ;Set RC5 LOW
call delay
bcf PORTC,5
call delay
goto loop

;————————————
AD ; A/D ROUTINE
MOVLW b'00000001' ;Left justified, Vdd Vref, AN0 source input
MOVWF ADCON0 ; A2D routine conversion
BSF ADCON0,GO ;Start conversion
BTFSC ADCON0,GO ;Is conversion done?
GOTO $-1 ;No, test again

MOVFW ADRESH ;Read upper 8 bits
MOVWF RESULTHI

BSF STATUS,RP0 ;Bank 1
MOVFW ADRESL ;Read lower 2 bits
BCF STATUS,RP0 ;Bank 0

movwf RESULTLO ; save result lo
return

delay:
call AD ; get values
incfsz RESULTLO,f ; incriment file to zero (inc instead of dec to reverse pot direction)
goto $-1 ; repeat

incfsz RESULTHI,f ;increment file to zero
goto $-3
return
Moderators note: used code tags
 
Last edited by a moderator:

JohnInTX

Joined Jun 26, 2012
4,787
You can't really just add it - the separate routines use the same AD channel and both use a dumb delay for timing themselves. Obviously, you can't use the same AD channel to read two different pots and using delay like you have shown consumes all of the processing power so the two routines, as you have found out, can't live together.

Two things have to happen here.
1) you have to change AD channels to select which pot to read for the two functions.
2) you have to figure out how to integrate the flasher into the already working variable duty cycle PWM code.
3) once the flasher timing is working, use it to control the on/off flashing of the PWM driven LED.

1 and 2 are already done and shown in the loop code I posted. There is a comment where the LED on/off goes. Since the LED is PWM driven, you can turn it off for the flashing by setting the duty cycle to 0 and leaving it at 0 during that 1/2 of the flash time. When the flasher indicates that the LED should be on, start polling the brightness pot and use it to turn the PWM on at the set brightness for the other 1/2 flash cycle.

The 'flasher' itself can be a one bit flag in RAM that gets toggled every time FlashK runs to 0 and gets reloaded. The brightness code looks at the flag and either sets the PWM to some brightness duty cycle or to 0 depending on the flasher flag.

Note that the dumb delay in the flasher is always 1 msec. FlashK is loaded by the flash rate AD1 to delay some number of 1 ms tiks. That way, you don't have to wait for the whole flash delay to update the brightness.

Take this one step at a time. Look at that loop code until you understand how it works for 2 channels. Then incorporate the PWM control (0 or some duty cycle for off/on). The reason you are having trouble adding your code above is that it isn't possible as written.

That's really all I can offer. I am working on a project myself.

What are you using for a debugger? PICkit?
 

Thread Starter

Don Omar

Joined May 5, 2017
49
You can't really just add it - the separate routines use the same AD channel and both use a dumb delay for timing themselves. Obviously, you can't use the same AD channel to read two different pots and using delay like you have shown consumes all of the processing power so the two routines, as you have found out, can't live together.

Two things have to happen here.
1) you have to change AD channels to select which pot to read for the two functions.
2) you have to figure out how to integrate the flasher into the already working variable duty cycle PWM code.
3) once the flasher timing is working, use it to control the on/off flashing of the PWM driven LED.

1 and 2 are already done and shown in the loop code I posted. There is a comment where the LED on/off goes. Since the LED is PWM driven, you can turn it off for the flashing by setting the duty cycle to 0 and leaving it at 0 during that 1/2 of the flash time. When the flasher indicates that the LED should be on, start polling the brightness pot and use it to turn the PWM on at the set brightness for the other 1/2 flash cycle.

The 'flasher' itself can be a one bit flag in RAM that gets toggled every time FlashK runs to 0 and gets reloaded. The brightness code looks at the flag and either sets the PWM to some brightness duty cycle or to 0 depending on the flasher flag.

Note that the dumb delay in the flasher is always 1 msec. FlashK is loaded by the flash rate AD1 to delay some number of 1 ms tiks. That way, you don't have to wait for the whole flash delay to update the brightness.

Take this one step at a time. Look at that loop code until you understand how it works for 2 channels. Then incorporate the PWM control (0 or some duty cycle for off/on). The reason you are having trouble adding your code above is that it isn't possible as written.

That's really all I can offer. I am working on a project myself.

What are you using for a debugger? PICkit?
yeah im using the PICkit3
 

JohnInTX

Joined Jun 26, 2012
4,787
all this things are new to me
when you say set the pwm to 0
what do you mean?
No worries. It is new to everyone at one time or another..

Think of it this way. Flashing the one LED at the specified brightness can be viewed at turning it on at a varying duty cycle and off by just setting the duty cycle to 0 yes? The PIC doesn't care if the PWM is still running or not, it's just running at 0% duty cycle. So my thought was to replace these comments in the source here

;----------------
; toggle LED here
;----------------

with code that toggles a bit in a (new) RAM register. On/off/on/off at the flash rate. That's all the 'flasher' has to do. Up above where you set the PWM to vary the brightness check that bit. If its is '1' turn the LED on by loading the PWM with the duty cycle. If 0, set the duty cycle to 000:
clrf CCPR1L ; to turn LED off, set duty cycle to 0
movlw B'11001111'
andwf CCP1CON,F

Nice.

Are you using the PK3 as a debugger? You can set a breakpoint in your code and inspect the various registers and variables to see what is going on.
 

LesJones

Joined Jan 8, 2017
4,511
The sample code I posted in post #8 shows selecting an analogue input channel. (It has 4 potentiometers, the slider of each connected to an analogue input channel) If you look at the "Read_Analog" subroutine you can see the analogue channel number being moved into the correct bit positions in ADCON0. NOTE the channel number bit positions in the ADCON register on the PIC that you are using are not the same on the PIC that you are using to the one that I have used. (I think they are one position to the left or right. So you will need one more or one less RLF instruction and you need to clear different bit positions before "ORing" in the shfted channel number.)

Les.
 

MMcLaren

Joined Feb 14, 2010
861
This is an interesting assignment. May I ask why you need 1024 PWM brightness levels, as opposed to 256 levels, please?

I suspect 256 LED brightness levels would be fine and I agree with John's method for turning the LED on or off each half cycle.

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  main loop                                                      ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

halfcyc
        incf    duration,W      ; duration, 1..256 msecs          |00
        movwf   counter         ;                                 |00
;
;  toggle LED on or off each half cycle
;
        movf    pwmlevel,W      ; duty cycle, 0..255              |00
        movf    CCPR1L,F        ; CCPR1L = 0 (off half cycle)?    |00
        skpz                    ; yes, skip, else                 |00
        movlw   0               ; use 'off' duty cycle value      |00
        movwf   CCPR1L          ; set PWM duty cycle              |00
waitcyc
        movlw   1<<CHS0         ; bit mask for ADCON0.CHS0 bit    |00
        xorwf   ADCON0,F        ; toggle channel (0 or 1)         |00
        DelayCy(1*msecs-20)     ; wait 1-msec minus 20 cycles     |00
;
;  read each ADC channel on alternating 1-msec intervals
;
        bsf     ADCON0,GO       ; start conversion                |00
adcwait btfsc   ADCON0,GO       ; done? yes, skip, else           |00
        goto    adcwait         ; branch (wait)                   |00
        movf    ADRESH,W        ; adc result, 0..255              |00
        btfsc   ADCON0,CHS0     ; duration? no, skip, else        |00
        movwf   duration        ; update 'duration', 0..255       |00
        btfss   ADCON0,CHS0     ; brightness? no, skip, else      |00
        movwf   pwmlevel        ; update 'pwmlevel', 0..255       |00
;
;  check for end of half cycle
;
        decfsz  counter,F       ; end of half cycle duration?     |00
        goto    waitcyc         ; no, branch, else, fall thru     |00
        goto    halfcyc         ; start a new half cycle          |00
 
Last edited:

Thread Starter

Don Omar

Joined May 5, 2017
49
This is an interesting assignment. May I ask why you need 1024 PWM brightness levels, as opposed to 256 levels, please?

I suspect 256 LED brightness levels would be fine and I agree with John's method for turning the LED on or off each half cycle.

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  main loop                                                      ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

halfcyc
        incf    duration,W      ; duration, 1..256 msecs          |00
        movwf   counter         ;                                 |00
;
;  toggle LED on or off each half cycle
;
        movf    pwmlevel,W      ; duty cycle, 0..255              |00
        movf    CCPR1L,F        ; CCPR1L = 0 (off half cycle)?    |00
        skpz                    ; yes, skip, else                 |00
        movlw   0               ; use 'off' duty cycle value      |00
        movwf   CCPR1L          ; set PWM duty cycle              |00
waitcyc
        movlw   1<<CHS0         ; bit mask for ADCON0.CHS0 bit    |00
        xorwf   ADCON0,F        ; toggle channel (0 or 1)         |00
        DelayCy(1*msecs-20)     ; wait 1-msec minus 20 cycles     |00
;
;  read each ADC channel on alternating 1-msec intervals
;
        bsf     ADCON0,GO       ; start conversion                |00
adcwait btfsc   ADCON0,GO       ; done? yes, skip, else           |00
        goto    adcwait         ; branch (wait)                   |00
        movf    ADRESH,W        ; adc result, 0..255              |00
        btfsc   ADCON0,CHS0     ; duration? no, skip, else        |00
        movwf   duration        ; update 'duration', 0..255       |00
        btfss   ADCON0,CHS0     ; brightness? no, skip, else      |00
        movwf   pwmlevel        ; update 'pwmlevel', 0..255       |00
;
;  check for end of half cycle
;
        decfsz  counter,F       ; end of half cycle duration?     |00
        goto    waitcyc         ; no, branch, else, fall thru     |00
        goto    halfcyc         ; start a new half cycle          |00


i tried this code seems to be working i think the led flashes really quickly you can hardly see it
when i turn the potentiometer plus i had to remove this bit DelayCy(1*msecs-20) ; wait 1-msec minus 20 cycles because the assembler does not read it as instruction
please let me now if i did something wrong ,i had to add variables to the ram register
 

MMcLaren

Joined Feb 14, 2010
861
That's just an example excerpt to demonstrate toggling the LED at the end of each half cycle. It can only become a working example if you fill in the missing pieces. Sorry!

The DelayCy() line is a macro call in a cycle-accurate delay sub-subsystem that allows specifying delays in cycles, microseconds, or milliseconds plus or minus 'n' cycles. Just replace it with a call to your own ~1-msec delay function. However, if you're interested, I've included a DelayCy() fixed delay sub-system demo below. The sub-system can be used to produce cycle-accurate delays in your code at almost any clock (4, 8, 16, 20, 32, etc.).

Code:
;******************************************************************
;  DelayCy() Demo, (C)2010, Mike McLaren                          *
;******************************************************************

        cblock 0x70
delayhi                 ; DelayCy() sub-system timing variable
        endc
;==================================================================
;  K8LH DelayCy() subsystem macro generates four instructions     =
;==================================================================
        radix   dec
clock   equ     4               ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     usecs*1000      ; cycles/millisecond multiplier
dloop   equ     5               ; loop size, 5 to ??? cycles
;
;  -- loop --  -- delay range --  -- memory overhead ----------
;  5-cyc loop, 11..327690 cycles,  9 words (+4 each macro call)
;  6-cyc loop, 11..393226 cycles, 10 words (+4 each macro call)
;  7-cyc loop, 11..458762 cycles, 11 words (+4 each macro call)
;  8-cyc loop, 11..524298 cycles, 12 words (+4 each macro call)
;  9-cyc loop, 11..589834 cycles, 13 words (+4 each macro call)
;
DelayCy macro   cycles          ; range, see above
    if (cycles<11)|(cycles>(dloop*65536+10))
        error " DelayCy range error "
    else
        movlw   high((cycles-11)/dloop)+1
        movwf   delayhi
        movlw   low ((cycles-11)/dloop)
        call    uLoop-((cycles-11)%dloop)
    endif
        endm

;******************************************************************
;  example code for simulation testing                            *
;******************************************************************
        org     0x000
SimTest
        DelayCy(20*msecs)       ; <- put simulator PC here
        goto    $               ; <- put simulator break point here

;******************************************************************
;  K8LH DelayCy() subsystem 16-bit 'uLoop' timing subroutine      *
;******************************************************************
a = dloop-1
    while a > 0
        nop                     ; (cycles-11)%dloop entry points  |B0
a -= 1
    endw
uLoop   addlw   -1              ; subtract 'dloop' loop time      |B0
        skpc                    ; borrow? no, skip, else          |B0
        decfsz  delayhi,F       ; done?  yes, skip, else          |B0
        goto    uLoop-dloop+5   ; do another loop                 |B0
        return                  ;                                 |B0

;******************************************************************
        end
 
Last edited:
Top