PDA

View Full Version : conversion



pramodsinha
- 1st January 2006, 05:27
Hi,
is it possible to start a thread where conversions can be done,programmes written in other language.
can somebody convert attached code from microchipc.com and explain the working:
i want to generate software pwm for push pull operation as well as do some other works like writing on lcd.
thanks
pramod

Bruce
- 18th January 2006, 19:48
Converting most C programs over to PBP is a fairly simple process. Here's a converted version with blinkng LEDs to indicate which task is currently being run. Change timing to suit whatever osc speed you use.

I left most of the C code in place for reference, and bumped task0 up to 100 ticks so you can actually see the task0 LED blinking.


' Conversion from C to BASIC
'//multitasking system – handle multiple tasks with one microprocessor

DEFINE OSC 4 ' Adjust this & TMR1 values for your osc freq
DEFINE LOADER_USED 1
DEFINE INTHAND ISR

'Variables for saving state in interrupt handler
wsave VAR BYTE $70 system ' Saves W
ssave VAR BYTE bank0 system ' Saves STATUS
psave VAR BYTE bank0 system ' Saves PCLATH
fsave VAR BYTE bank0 system ' Saves FSR

'//task counters used to tell when a task is ready to be executed
'//all these counters are incremented every time a 500us interrupt happens
'//every task has its own counter that is updated every time a 500us interrupt happens
'unsigned int task0_counter=0;
task0_counter VAR BYTE
task0_counter = 0
'unsigned int task1_counter=0;
task1_counter VAR WORD
task1_counter = 0
'unsigned int task2_counter=0;
task2_counter VAR WORD
task2_counter = 0

FALSE CON 0
TRUE CON 1

'//this tells when a task is going to happen again
'//for example, when task0_counter==TASK0_COUNTER_MAX, set task0_counter=0 and do task
'#define TASK0_COUNTER_MAX 1 //high frequency task – every 500us, maybe PWM
'#define TASK1_COUNTER_MAX 2000 //low frequency task – every 1000ms
'#define TASK2_COUNTER_MAX 5000 //low frequency and low priority task, every 2500ms
TASK2_COUNTER_MAX CON 5000
TASK1_COUNTER_MAX CON 2000
TASK0_COUNTER_MAX CON 100

'#define TICKS_BETWEEN_INTERRUPTS 2000
'#define INTERRUPT_OVERHEAD 19
'#define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
'#define TMR1RESET_HIGH TMR1RESET >> 8
'#define TMR1RESET_LOW TMR1RESET & 0xFF
TICKS_BETWEEN_INTERRUPTS CON 2000
INTERRUPT_OVERHEAD CON 19
INTER CON TICKS_BETWEEN_INTERRUPTS - INTERRUPT_OVERHEAD
TMR1RESET CON $FFFF-INTER '$FFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD)
TMR1RESET_HIGH CON TMR1RESET >> 8
TMR1RESET_LOW CON TMR1RESET & $FF

'//this enables/disables a task
'volatile unsigned char task0_enable=TRUE;
'volatile unsigned char task1_enable=TRUE;
'volatile unsigned char task2_enable=TRUE;
'//this allows tasks triggered by interrupt to run in the background in main()
'volatile unsigned char task2_go=FALSE;
task0_enable VAR BIT
task0_enable = TRUE
task1_enable VAR BIT
task1_enable = TRUE
task2_enable VAR BIT
task2_enable = TRUE
task2_go VAR BIT
task2_go = FALSE

SYMBOL TMR1ON = T1CON.0
SYMBOL TMR1IF = PIR1.0
SYMBOL TMR1IE = PIE1.0
SYMBOL TMR1CS = T1CON.1
SYMBOL T1CKPS0 = T1CON.4
SYMBOL T1CKPS1 = T1CON.5
SYMBOL PEIE = INTCON.6
SYMBOL GIE = INTCON.7

PORTB = 0
TRISB = 0

GOTO Main

setup_multitasking:
'void setup_multitasking(void)
'{
' //set up tmr1 to interrupt every 500us
TMR1CS=0;
T1CKPS0=0;
T1CKPS1=0;
'/*We want to wait 2000 clock cycles, or 500us @ 16MHz (instructions are 1/4 speed of clock). Timer 1 interrupts when it gets to 0xFFFF or 65535. Therefore, we set timer 1 to 65535 minus 2000 = 63535, then wait 2000 ticks until rollover at 65535. To test, use simulator to find that its exactly correct*/
' #define TICKS_BETWEEN_INTERRUPTS 2000
' #define INTERRUPT_OVERHEAD 19
' #define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
' #define TMR1RESET_HIGH TMR1RESET >> 8
' #define TMR1RESET_LOW TMR1RESET & 0xFF
TMR1ON=0;
TMR1H = TMR1RESET_HIGH
TMR1L = TMR1RESET_LOW
TMR1ON=1;
TMR1IF=0;
TMR1IE=1;
PEIE=1;
GIE=1;
'}
RETURN

ASM
ISR ; NOTE: If you're using BASIC in this ISR, then you may need to save
; some PBP system vars. Look for Darrel Taylors examples here in the forum.

; Uncomment the following if the device has less than 2k of code space
;movwf wsave ; Save W
;swapf STATUS, W ; Swap STATUS to W (swap avoids changing STATUS)
;clrf STATUS ; Clear STATUS
;movwf ssave ; Save swapped STATUS
;movf PCLATH, W ; Move PCLATH to W
;movwf psave ; Save PCLATH

; Save the FSR value for later
movf FSR, W ; Move FSR to W
movwf fsave ; Save FSR
ENDASM

'void interrupt isr(void)
'{
' //one tick every 500us at 16Mhz
IF TMR1IF THEN 'if (TMR1IF)
' {
' //set up timer 1 again to interrupt 500us in future
TMR1IF=0;
TMR1ON=0;
TMR1H=TMR1RESET_HIGH;
TMR1L=TMR1RESET_LOW;
TMR1ON=1;
task0_counter = task0_counter +1 'task0_counter++;
if (task0_counter>=TASK0_COUNTER_MAX) THEN ' //high frequency task – every 1 tick
' {
task0_counter=0;
if (task0_enable==TRUE) THEN
' {
' //do high frequency important task 0, for example PWM
TOGGLE 0 ' Toggle PORTB.0
' }
ENDIF
' }
ENDIF
task1_counter = task1_counter + 1 'task1_counter++;
if (task1_counter>=TASK1_COUNTER_MAX) THEN ' //low priority task - every 2000 ticks
' {
task1_counter=0;
if (task1_enable==TRUE) THEN
' {
' //do low frequency yet important task 1
TOGGLE 1 ' Toggle PORTB.1
' }
ENDIF
' }
ENDIF

'/*this task takes a long time, 100ms for example, lots of maths. Is extremely low priority, but has to be done at regular intervals, so all this does is trigger it. In main(), it will, at leisure, poll ‘task2_go’ and then execute it in the background.*/
task2_counter = task2_counter + 1 'task2_counter++;
if (task2_counter>=TASK2_COUNTER_MAX) THEN ' //every 250ms
' {
task2_counter=0;
if (task2_enable==TRUE) THEN
' {
TOGGLE 2 ' Toggle PORTB.2
' //every 250ms take 100ms to do maths, do this in main() so the we can get back to doing the high frequency tasks.
task2_go=TRUE;
' }
ENDIF
' }
ENDIF
' } //if (TMR1IF)
'} //interrupt routine
ENDIF

ASM
; Restore FSR, PCLATH, STATUS and W registers
finished
movf fsave, W ; retrieve FSR value
movwf FSR ; Restore it to FSR
movf psave, W ; Retrieve PCLATH value
movwf PCLATH ; Restore it to PCLATH
swapf ssave, W ; Retrieve the swapped STATUS value (swap to avoid changing STATUS)
movwf STATUS ; Restore it to STATUS
swapf wsave, F ; Swap the stored W value
swapf wsave, W ; Restore it to W (swap to avoid changing STATUS)
retfie ; Return from the interrupt
EndAsm

Main:
'main()
'{
'setup_multitasking();
GOSUB setup_multitasking:

Idle:
if (task2_go==TRUE) THEN
task2_go=FALSE;
ENDIF
GOTO Idle
' //take our time, doing heaps of complex maths at our leisure in the background
' }
' }
'}
END
Look for some of Darrel Taylors (or Tim Box) interrupt saving routines here in the forum if you're using BASIC commands with assembler interrupts.

Depending on which PBP commands you use, you'll most likely need to save some PBP system variables.

I suspect this would also work fine with ON INTERRUPT, but I hate using it.

pramodsinha
- 19th January 2006, 15:58
Converting most C programs over to PBP is a fairly simple process. Here's a converted version with blinkng LEDs to indicate which task is currently being run. Change timing to suit whatever osc speed you use.

I left most of the C code in place for reference, and bumped task0 up to 100 ticks so you can actually see the task0 LED blinking.


' Conversion from C to BASIC
'//multitasking system – handle multiple tasks with one microprocessor

DEFINE OSC 4 ' Adjust this & TMR1 values for your osc freq
DEFINE LOADER_USED 1
DEFINE INTHAND ISR

'Variables for saving state in interrupt handler
wsave VAR BYTE $70 system ' Saves W
ssave VAR BYTE bank0 system ' Saves STATUS
psave VAR BYTE bank0 system ' Saves PCLATH
fsave VAR BYTE bank0 system ' Saves FSR

'//task counters used to tell when a task is ready to be executed
'//all these counters are incremented every time a 500us interrupt happens
'//every task has its own counter that is updated every time a 500us interrupt happens
'unsigned int task0_counter=0;
task0_counter VAR BYTE
task0_counter = 0
'unsigned int task1_counter=0;
task1_counter VAR WORD
task1_counter = 0
'unsigned int task2_counter=0;
task2_counter VAR WORD
task2_counter = 0

FALSE CON 0
TRUE CON 1

'//this tells when a task is going to happen again
'//for example, when task0_counter==TASK0_COUNTER_MAX, set task0_counter=0 and do task
'#define TASK0_COUNTER_MAX 1 //high frequency task – every 500us, maybe PWM
'#define TASK1_COUNTER_MAX 2000 //low frequency task – every 1000ms
'#define TASK2_COUNTER_MAX 5000 //low frequency and low priority task, every 2500ms
TASK2_COUNTER_MAX CON 5000
TASK1_COUNTER_MAX CON 2000
TASK0_COUNTER_MAX CON 100

'#define TICKS_BETWEEN_INTERRUPTS 2000
'#define INTERRUPT_OVERHEAD 19
'#define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
'#define TMR1RESET_HIGH TMR1RESET >> 8
'#define TMR1RESET_LOW TMR1RESET & 0xFF
TICKS_BETWEEN_INTERRUPTS CON 2000
INTERRUPT_OVERHEAD CON 19
INTER CON TICKS_BETWEEN_INTERRUPTS - INTERRUPT_OVERHEAD
TMR1RESET CON $FFFF-INTER '$FFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD)
TMR1RESET_HIGH CON TMR1RESET >> 8
TMR1RESET_LOW CON TMR1RESET & $FF

'//this enables/disables a task
'volatile unsigned char task0_enable=TRUE;
'volatile unsigned char task1_enable=TRUE;
'volatile unsigned char task2_enable=TRUE;
'//this allows tasks triggered by interrupt to run in the background in main()
'volatile unsigned char task2_go=FALSE;
task0_enable VAR BIT
task0_enable = TRUE
task1_enable VAR BIT
task1_enable = TRUE
task2_enable VAR BIT
task2_enable = TRUE
task2_go VAR BIT
task2_go = FALSE

SYMBOL TMR1ON = T1CON.0
SYMBOL TMR1IF = PIR1.0
SYMBOL TMR1IE = PIE1.0
SYMBOL TMR1CS = T1CON.1
SYMBOL T1CKPS0 = T1CON.4
SYMBOL T1CKPS1 = T1CON.5
SYMBOL PEIE = INTCON.6
SYMBOL GIE = INTCON.7

PORTB = 0
TRISB = 0

GOTO Main

setup_multitasking:
'void setup_multitasking(void)
'{
' //set up tmr1 to interrupt every 500us
TMR1CS=0;
T1CKPS0=0;
T1CKPS1=0;
'/*We want to wait 2000 clock cycles, or 500us @ 16MHz (instructions are 1/4 speed of clock). Timer 1 interrupts when it gets to 0xFFFF or 65535. Therefore, we set timer 1 to 65535 minus 2000 = 63535, then wait 2000 ticks until rollover at 65535. To test, use simulator to find that its exactly correct*/
' #define TICKS_BETWEEN_INTERRUPTS 2000
' #define INTERRUPT_OVERHEAD 19
' #define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
' #define TMR1RESET_HIGH TMR1RESET >> 8
' #define TMR1RESET_LOW TMR1RESET & 0xFF
TMR1ON=0;
TMR1H = TMR1RESET_HIGH
TMR1L = TMR1RESET_LOW
TMR1ON=1;
TMR1IF=0;
TMR1IE=1;
PEIE=1;
GIE=1;
'}
RETURN

ASM
ISR ; NOTE: If you're using BASIC in this ISR, then you may need to save
; some PBP system vars. Look for Darrel Taylors examples here in the forum.

; Uncomment the following if the device has less than 2k of code space
;movwf wsave ; Save W
;swapf STATUS, W ; Swap STATUS to W (swap avoids changing STATUS)
;clrf STATUS ; Clear STATUS
;movwf ssave ; Save swapped STATUS
;movf PCLATH, W ; Move PCLATH to W
;movwf psave ; Save PCLATH

; Save the FSR value for later
movf FSR, W ; Move FSR to W
movwf fsave ; Save FSR
ENDASM

'void interrupt isr(void)
'{
' //one tick every 500us at 16Mhz
IF TMR1IF THEN 'if (TMR1IF)
' {
' //set up timer 1 again to interrupt 500us in future
TMR1IF=0;
TMR1ON=0;
TMR1H=TMR1RESET_HIGH;
TMR1L=TMR1RESET_LOW;
TMR1ON=1;
task0_counter = task0_counter +1 'task0_counter++;
if (task0_counter>=TASK0_COUNTER_MAX) THEN ' //high frequency task – every 1 tick
' {
task0_counter=0;
if (task0_enable==TRUE) THEN
' {
' //do high frequency important task 0, for example PWM
TOGGLE 0 ' Toggle PORTB.0
' }
ENDIF
' }
ENDIF
task1_counter = task1_counter + 1 'task1_counter++;
if (task1_counter>=TASK1_COUNTER_MAX) THEN ' //low priority task - every 2000 ticks
' {
task1_counter=0;
if (task1_enable==TRUE) THEN
' {
' //do low frequency yet important task 1
TOGGLE 1 ' Toggle PORTB.1
' }
ENDIF
' }
ENDIF

'/*this task takes a long time, 100ms for example, lots of maths. Is extremely low priority, but has to be done at regular intervals, so all this does is trigger it. In main(), it will, at leisure, poll ‘task2_go’ and then execute it in the background.*/
task2_counter = task2_counter + 1 'task2_counter++;
if (task2_counter>=TASK2_COUNTER_MAX) THEN ' //every 250ms
' {
task2_counter=0;
if (task2_enable==TRUE) THEN
' {
TOGGLE 2 ' Toggle PORTB.2
' //every 250ms take 100ms to do maths, do this in main() so the we can get back to doing the high frequency tasks.
task2_go=TRUE;
' }
ENDIF
' }
ENDIF
' } //if (TMR1IF)
'} //interrupt routine
ENDIF

ASM
; Restore FSR, PCLATH, STATUS and W registers
finished
movf fsave, W ; retrieve FSR value
movwf FSR ; Restore it to FSR
movf psave, W ; Retrieve PCLATH value
movwf PCLATH ; Restore it to PCLATH
swapf ssave, W ; Retrieve the swapped STATUS value (swap to avoid changing STATUS)
movwf STATUS ; Restore it to STATUS
swapf wsave, F ; Swap the stored W value
swapf wsave, W ; Restore it to W (swap to avoid changing STATUS)
retfie ; Return from the interrupt
EndAsm

Main:
'main()
'{
'setup_multitasking();
GOSUB setup_multitasking:

Idle:
if (task2_go==TRUE) THEN
task2_go=FALSE;
ENDIF
GOTO Idle
' //take our time, doing heaps of complex maths at our leisure in the background
' }
' }
'}
END
Look for some of Darrel Taylors (or Tim Box) interrupt saving routines here in the forum if you're using BASIC commands with assembler interrupts.

Depending on which PBP commands you use, you'll most likely need to save some PBP system variables.

I suspect this would also work fine with ON INTERRUPT, but I hate using it.

THANKS A LOT BRUCE


THANKS BRUCE,
I AM GOING THROUGH YOUR SUGGESTIONS AND HOPE THIS WOULD HELP
TOO MANY PICKERS WHO WISH TO IMPLEMENT MULTITASKING IN THEIR PROJECTS.
I AM A LEARNER AND I WILL DO SOME EXPERIMENTS WITH YOUR ADVICE.
THANKS AGAIN
REGARDS
PRAMOD